From dc10c78a8319df64bb03dfef913c2601486549c0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 10 Mar 2008 09:28:58 +0100 Subject: [PATCH 01/40] merge Lumiera renaming --- DIR_INFO | 2 +- INSTALL | 4 ++-- LICENSE | 11 ++--------- README | 29 ++++++++++++++++++----------- SConstruct | 30 +++++++++++++++--------------- admin/scons/Buildhelper.py | 8 ++++---- bin/DIR_INFO | 2 +- oldsrc/DIR_INFO | 2 +- src/lib/DIR_INFO | 2 +- src/main.cpp | 14 ++++++++------ src/nobugcfg.h | 6 +++--- src/plugin/helloplugin.c | 4 ++-- src/tool/DIR_INFO | 2 +- src/tool/SConscript | 4 ---- src/tool/hello.c | 4 ++-- tests/test.sh | 2 +- 16 files changed, 62 insertions(+), 64 deletions(-) diff --git a/DIR_INFO b/DIR_INFO index 39fd1850f..f05e9a881 100644 --- a/DIR_INFO +++ b/DIR_INFO @@ -1 +1 @@ -The root dir for the cinelerra3 project +The root dir for the Lumiera project diff --git a/INSTALL b/INSTALL index c6cf3ff92..7983166ec 100644 --- a/INSTALL +++ b/INSTALL @@ -1,5 +1,5 @@ -Running / Installing Cinelerra-3 Prototype -========================================== +Running / Installing Lumiera Prototype +====================================== ** to be written ** diff --git a/LICENSE b/LICENSE index b93cee22f..4a684524d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,17 +1,10 @@ -The Cinelerra source code is (C) by the original authors (see AUTHORS) +The Lumiera source code is (C) by the original authors (see AUTHORS) ======================================================================= -Cinelerra is free software -- you may use and redistribute it under the +Lumiera is free software -- you may use and redistribute it under the Terms and conditions of the GNU GENERAL PUBLIC LICENSE (GPL); either version 2 of the License, or (at your option) any later version. ======================================================================= For a copy of the GPL Version 2 see the file "COPYING" -In addition to the GPL's warranty stipulation, Cinelerra is distributed WITHOUT -GUARANTEED SUPPORT; without even the guarantee of ADDITIONAL LABOR. Support -that is not guaranteed includes technical support, compiler troubleshooting, -debugging, version matching, updating, among other additional labor which -may or may not be required to meet a user's requirements. - - diff --git a/README b/README index 0ab5fe3ae..7913094ee 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ -====================================== -Cinelerra -- the video NLE for Linux -====================================== +==================================== +Lumiera -- the video NLE for Linux +==================================== - Cinelerra is a nonlinear video editing and compositing tool. + Lumiera is a nonlinear video editing and compositing tool. It understands some of the common multimedia formats (quicktime, avi, ogg) and audio/video compression codecs (divx, xvid, mpeg1/2/4, ...) @@ -12,23 +12,30 @@ Cinelerra -- the video NLE for Linux and RGB-float colormodels and the ability to mix media with differing sizes and framerates. +For more information about Lumiera visit http://lumiera.org/ For more information about Cinelerra visit http://cinelerra.org/ ------------------------------- -"Cinelerra-3" prototype code ------------------------------- +---------------------------- +"Lumiera" prototype code +---------------------------- -**This source tree doesn't contain a working video editing application** -Rather, it contains some design studies and prototype code for improving -some aspects of the Cinelerra Application. This work may eventually -evolve into a new major release of Cinelerra. +**This source tree doesn't yet contain a working video editing application** +Rather, it contains the initial framework and core modules of the lower and +middle layer of the envisioned new Application "Lumiera". As of 7/2007, we start here with some backend and render engine modules together with some unit tests. You should find a wiki with detailed design considerations and developer documentation and a UML model (usable with BOUML 2.29) in the sibling directories. +As of 2/2008 the project has been separated completely from his ancestor "Cinelerra" +The Community, which is largely identical to the Cinelerra-CV community, choose the +new project name "Lumiera". The basic project infrastructure is up and running, +and work on the new codebase is going on continuosely. We can show nothing but +a running test suite for now. + + Build Requirements ------------------ diff --git a/SConstruct b/SConstruct index 5dc55580a..4d2f2ee9c 100644 --- a/SConstruct +++ b/SConstruct @@ -1,10 +1,10 @@ # -*- python -*- ## -## SConstruct - SCons based build-sytem for Cinelerra +## SConstruct - SCons based build-sytem for Lumiera ## -# Copyright (C) CinelerraCV -# 2007, Hermann Vosseler +# Copyright (C) Lumiera.org +# 2008, Hermann Vosseler # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -61,7 +61,7 @@ def setupBasicEnvironment(): , SRCDIR=SRCDIR , BINDIR=BINDIR , CPPPATH=["#"+SRCDIR] # used to find includes, "#" means always absolute to build-root - , CPPDEFINES=['-DCINELERRA_VERSION='+VERSION ] # note: it's a list to append further defines + , CPPDEFINES=['-DLUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall ' # -fdiagnostics-show-option ) @@ -134,7 +134,7 @@ def defineCmdlineOptions(): def prepareOptionsHelp(opts,env): prelude = """ USAGE: scons [-c] [OPTS] [key=val [key=val...]] [TARGETS] - Build and optionally install Cinelerra. + Build and optionally install Lumiera. Without specifying any target, just the (re)build target will run. Add -c to the commandline to clean up anything a given target would produce @@ -234,21 +234,21 @@ def defineBuildTargets(env, artifacts): We use a custom function to declare a whole tree of srcfiles. """ - cinobj = ( srcSubtree(env,'$SRCDIR/backend') + lumobj = ( srcSubtree(env,'$SRCDIR/backend') + srcSubtree(env,'$SRCDIR/proc') + srcSubtree(env,'$SRCDIR/common') + srcSubtree(env,'$SRCDIR/lib') ) plugobj = srcSubtree(env,'$SRCDIR/plugin', isShared=True) - core = env.StaticLibrary('$BINDIR/core.la', cinobj) - #core = cinobj # use this for linking directly + core = env.StaticLibrary('$BINDIR/core.la', lumobj) + #core = lumobj # use this for linking directly # use PCH to speed up building precomp = env.PrecompiledHeader('$SRCDIR/pre') - env.Depends(cinobj, precomp) + env.Depends(lumobj, precomp) - artifacts['cinelerra'] = env.Program('$BINDIR/cinelerra', ['$SRCDIR/main.cpp']+ core ) - artifacts['plugins'] = env.SharedLibrary('$BINDIR/cinelerra-plugin', plugobj) + artifacts['lumiera'] = env.Program('$BINDIR/lumiera', ['$SRCDIR/main.cpp']+ core ) + artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', plugobj) # call subdir SConscript(s) for independent components SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts') @@ -264,7 +264,7 @@ def definePostBuildTargets(env, artifacts): il = env.Alias('install-lib', '$DESTDIR/lib') env.Alias('install', [ib, il]) - build = env.Alias('build', artifacts['cinelerra']+artifacts['plugins']) + build = env.Alias('build', artifacts['lumiera']+artifacts['plugins']) allbu = env.Alias('allbuild', build+artifacts['testsuite']) env.Default('build') # additional files to be cleaned when cleaning 'build' @@ -286,11 +286,11 @@ def definePostBuildTargets(env, artifacts): def defineInstallTargets(env, artifacts): """ define some artifacts to be installed into target locations. """ - env.Install(dir = '$DESTDIR/bin', source=artifacts['cinelerra']) + env.Install(dir = '$DESTDIR/bin', source=artifacts['lumiera']) env.Install(dir = '$DESTDIR/lib', source=artifacts['plugins']) env.Install(dir = '$DESTDIR/bin', source=artifacts['tools']) - env.Install(dir = '$DESTDIR/share/doc/cinelerra$VERSION/devel', source=artifacts['doxydoc']) + env.Install(dir = '$DESTDIR/share/doc/lumiera$VERSION/devel', source=artifacts['doxydoc']) ##################################################################### @@ -310,7 +310,7 @@ artifacts = {} # the various things we build. # Each entry actually is a SCons-Node list. # Passing these entries to other builders defines dependencies. -# 'cinelerra' : the App +# 'lumiera' : the App # 'plugins' : plugin shared lib # 'tools' : small tool applications (e.g mpegtoc) # 'src,tar' : source tree as tarball (without doc) diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index 419de6e68..ae3f5fb1f 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -3,8 +3,8 @@ ## Buildhelper.py - helpers, custom builders, for SConstruct ## -# Copyright (C) CinelerraCV -# 2007, Hermann Vosseler +# Copyright (C) Lumiera.org +# 2008, Hermann Vosseler # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -123,8 +123,8 @@ def Tarball(env,location,dirs,suffix=''): """ targetID = '../extern-tar%s' % suffix versionID = env['VERSION'] - defaultName = 'cinelerra%s_%s' % (suffix, versionID) - nameprefix = 'cinelerra-%s/' % (versionID) + defaultName = 'lumiera%s_%s' % (suffix, versionID) + nameprefix = 'lumiera-%s/' % (versionID) location = env.subst(location) dirs = env.subst(dirs) return env.Command(targetID,None, createTarball, diff --git a/bin/DIR_INFO b/bin/DIR_INFO index c9048f8c1..9dafe61c3 100644 --- a/bin/DIR_INFO +++ b/bin/DIR_INFO @@ -1 +1 @@ -cinelerra executable(s) and libraries will be built here +Lumiera executable(s) and libraries will be built here diff --git a/oldsrc/DIR_INFO b/oldsrc/DIR_INFO index 666124344..e3f9d5b47 100644 --- a/oldsrc/DIR_INFO +++ b/oldsrc/DIR_INFO @@ -1 +1 @@ -cinelerra2 sources, added per case when needed +Cinelerra2 sources, added per case when needed diff --git a/src/lib/DIR_INFO b/src/lib/DIR_INFO index 1bc5bfed3..6946a8b7e 100644 --- a/src/lib/DIR_INFO +++ b/src/lib/DIR_INFO @@ -1,2 +1,2 @@ -cinelerra support library +Lumiera support library This dir contains the code for some tools and library code which will be used in many places diff --git a/src/main.cpp b/src/main.cpp index 18384f0c7..398cdc3df 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ /* - main.cpp - start the Cinelerra Application + main.cpp - start the Lumiera Application - Copyright (C) CinelerraCV - 2007, Christian Thaeter + Copyright (C) Lumiera.org + 2008, Christian Thaeter Hermann Vosseler This program is free software; you can redistribute it and/or @@ -23,14 +23,16 @@ #include +#include "lumiera.h" + using std::cout; using std::endl; +using lumiera::Appconfig; int main (int argc, char* argv[]) { - cout << "*** Cinelerra NLE for Linux ***" << endl - << " hello SCONS build system" << endl; + cout << "*** Lumiera NLE for Linux ***" << endl + << " Version: " << Appconfig::get("version") << endl; return 0; } - diff --git a/src/nobugcfg.h b/src/nobugcfg.h index 7907d50a1..916f28663 100644 --- a/src/nobugcfg.h +++ b/src/nobugcfg.h @@ -2,8 +2,8 @@ NOBUGCFG.h - global configuration and definitions for NoBug - Copyright (C) CinelerraCV - 2007, Christian Thaeter + Copyright (C) Lumiera.org + 2008, Christian Thaeter Hermann Vosseler This program is free software; you can redistribute it and/or @@ -27,7 +27,7 @@ ** The idea is that configuration and some globally used flag ** declarations are to be kept in one central location. Normally, ** this header will be included by appconfig.hpp, which in turn gets - ** included by cinelerra.h + ** included by lumiera.h ** @par Besides the usual guarded declarations, this header contains ** one section with the corresponding definitions. This section ** is to be called by appconfig.cpp only, which deals with application diff --git a/src/plugin/helloplugin.c b/src/plugin/helloplugin.c index 62a033f4e..d75f55f94 100644 --- a/src/plugin/helloplugin.c +++ b/src/plugin/helloplugin.c @@ -1,12 +1,12 @@ /* * hello.c - demonstrates how to build a standalone tool (C source) - * integrated into the SCons based build system of Cinelerra + * integrated into the SCons based build system of Lumiera */ #include int main(int argc, char* argv[]) { - printf("hello cinelerra world"); + printf("hello lumiera world"); return 0; } diff --git a/src/tool/DIR_INFO b/src/tool/DIR_INFO index 76e84db53..c8933be3e 100644 --- a/src/tool/DIR_INFO +++ b/src/tool/DIR_INFO @@ -1 +1 @@ -source code of supplementary tools to be built alongside with Cinelerra +source code of supplementary tools to be built alongside with Lumiera diff --git a/src/tool/SConscript b/src/tool/SConscript index fe3a35dcc..738483498 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -5,10 +5,6 @@ Import('env','artifacts') -# at the moment (8/07), Ichthyo tries to find out how to configure NoBug -# does it help, if we set the NOBUG_LOG to the environment used for building?? -env = env.Clone() -env['ENV']['NOBUG_LOG'] = 'ttt:WARNING' # build the ubiquitous Hello World application (note: C source) artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') diff --git a/src/tool/hello.c b/src/tool/hello.c index 62a033f4e..d75f55f94 100644 --- a/src/tool/hello.c +++ b/src/tool/hello.c @@ -1,12 +1,12 @@ /* * hello.c - demonstrates how to build a standalone tool (C source) - * integrated into the SCons based build system of Cinelerra + * integrated into the SCons based build system of Lumiera */ #include int main(int argc, char* argv[]) { - printf("hello cinelerra world"); + printf("hello lumiera world"); return 0; } diff --git a/tests/test.sh b/tests/test.sh index 1e4256eb2..b692b3959 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (C) CinelerraCV +# Copyright (C) Lumiera.org # 2007, Christian Thaeter # Hermann Vosseler # From bfa4b2bc70d49405f8da98a67ef409f1f0a287df Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 11 Jul 2008 05:01:56 +0200 Subject: [PATCH 02/40] (irrelevant) adjustments to make the SCons build of the nonexistant source happen --- SConstruct | 1 - src/main.cpp | 10 ++-------- src/proc/lumiera.hpp | 5 +---- src/proc/nobugcfg.hpp | 4 +--- tests/00test.tests | 11 +++++++++++ tests/SConscript | 8 ++++---- tests/bugs/buggy.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 tests/bugs/buggy.cpp diff --git a/SConstruct b/SConstruct index cef5ebda0..b76e0101e 100644 --- a/SConstruct +++ b/SConstruct @@ -247,7 +247,6 @@ def defineBuildTargets(env, artifacts): # use PCH to speed up building precomp = ( env.PrecompiledHeader('$SRCDIR/pre') - + env.PrecompiledHeader('$SRCDIR/pre_a') ) env.Depends(objproc, precomp) env.Depends(objlib, precomp) diff --git a/src/main.cpp b/src/main.cpp index 0e04ef89f..ffa3957f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,21 +26,15 @@ #include "proc/lumiera.hpp" using std::cout; -using std::endl; -using lumiera::Appconfig; -using lumiera::ON_GLOBAL_INIT; -using lumiera::ON_GLOBAL_SHUTDOWN; int main (int argc, char* argv[]) { - cout << "*** Lumiera NLE for Linux ***" << endl - << " Version: " << Appconfig::get("version") << "\n"; + cout << "*** Lumiera NLE for Linux ***" << "\n" + << " Version: quasi niente" << "\n"; - Appconfig::lifecycle (ON_GLOBAL_INIT); // great things are happening here.... - Appconfig::lifecycle (ON_GLOBAL_SHUTDOWN); return 0; } diff --git a/src/proc/lumiera.hpp b/src/proc/lumiera.hpp index b56ab69b6..f67ec719b 100644 --- a/src/proc/lumiera.hpp +++ b/src/proc/lumiera.hpp @@ -30,10 +30,7 @@ /* common types frequently used... */ -#include "common/util.hpp" -#include "common/time.hpp" -#include "common/error.hpp" ///< pulls in NoBug via nobugcfg.hpp -#include "lib/appconfig.hpp" +#include "proc/nobugcfg.hpp" namespace lumiera diff --git a/src/proc/nobugcfg.hpp b/src/proc/nobugcfg.hpp index 567838320..aa9411471 100644 --- a/src/proc/nobugcfg.hpp +++ b/src/proc/nobugcfg.hpp @@ -45,8 +45,6 @@ #include #include -#include "lib/appconfig.hpp" -#include "common/error.hpp" ///< make assertions throw instead of abort() /* declare flags used throughout the code base... */ @@ -61,7 +59,7 @@ namespace lumiera { void initialize_NoBug (); namespace { - LifecycleHook schedule_ (ON_BASIC_INIT, &initialize_NoBug); +// LifecycleHook schedule_ (ON_BASIC_INIT, &initialize_NoBug); } } #endif /*NOBUGCFG_H ======= (End) Part 1: DECLARATIONS ======== */ diff --git a/tests/00test.tests b/tests/00test.tests index 4d8781629..60b4b26d1 100644 --- a/tests/00test.tests +++ b/tests/00test.tests @@ -11,3 +11,14 @@ err: /bin/cat: ,nonexistent_file: No such file or directory return: 1 END + +TESTING "Run the Dummy Test Executable" ./test-bugs + +TEST "I am a dummy test" < + Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#include + +#include "proc/lumiera.hpp" + +using std::cout; + + +int main (int argc, char* argv[]) +{ + cout << "*** Lumiera NLE for Linux ***" << "\n" + << " This is a dummy test" << "\n"; + + + // great things are happening here.... + + return 2; +} From 50f8be0e161d7b6fe839692ee2801590305da19d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 11 Jul 2008 05:20:22 +0200 Subject: [PATCH 03/40] merge after integrating backend/library work --- SConstruct | 3 ++- admin/vgsuppression.c | 1 - tests/00test.tests | 1 + tests/SConscript | 6 ++++- tests/test.h | 52 +++++++++++++++++++++++++++++++++++++++++++ tests/test.sh | 3 +-- 6 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 tests/test.h diff --git a/SConstruct b/SConstruct index b76e0101e..8434b0a98 100644 --- a/SConstruct +++ b/SConstruct @@ -33,7 +33,7 @@ CUSTOPTIONSFILE = 'custom-options' SRCDIR = 'src' BINDIR = 'bin' TESTDIR = 'tests' -VERSION = '3+alpha.01' +VERSION = '0.1+pre.01' #-----------------------------------Configuration # NOTE: scons -h for help. @@ -68,6 +68,7 @@ def setupBasicEnvironment(): RegisterPrecompiledHeader_Builder(env) handleNoBugSwitches(env) + env.Append(CPPDEFINES = '_GNU_SOURCE') appendCppDefine(env,'DEBUG','DEBUG', 'NDEBUG') appendCppDefine(env,'OPENGL','USE_OPENGL') appendVal(env,'ARCHFLAGS', 'CCFLAGS') # for both C and C++ diff --git a/admin/vgsuppression.c b/admin/vgsuppression.c index 0e1f41fca..cad026e52 100644 --- a/admin/vgsuppression.c +++ b/admin/vgsuppression.c @@ -18,7 +18,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define _GNU_SOURCE /* just place any problematic calls where valgrind whines about in main (with comments please) diff --git a/tests/00test.tests b/tests/00test.tests index 60b4b26d1..d1205b8c1 100644 --- a/tests/00test.tests +++ b/tests/00test.tests @@ -22,3 +22,4 @@ END PLANNED "this may never happen" END + diff --git a/tests/SConscript b/tests/SConscript index 44d34e047..5a7c0145d 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -9,6 +9,10 @@ from Buildhelper import globRootdirs Import('env','artifacts','core') +# temp fix to add test.h -- wouldn't it be better to put this header be into src/lib ? +env = env.Clone() +env.Append(CPPPATH='#/.') # add Rootdir to Includepath, so test/test.h is found +# temp fix------------- def testExecutable(env,tree, exeName=None, obj=None): """ declare all targets needed to create a standalone @@ -52,7 +56,7 @@ def treatPluginTestcase(env): moduledirs = globRootdirs('*') # but have to treat some subdirs individually. -specials = ['plugin','locking','library'] +specials = ['plugin','locking','library','backend'] diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 000000000..d25fa0a59 --- /dev/null +++ b/tests/test.h @@ -0,0 +1,52 @@ +/* + test.h - macros for running tests + + Copyright (C) CinelerraCV + 2008, Christian Thaeter + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef TEST_H +#define TEST_H + +#include + +#include "lib/error.h" + +LUMIERA_ERROR_DEFINE (TEST, "test error"); + +#define TESTS_BEGIN \ +int \ +main (int argc, char** argv) \ +{ \ + NOBUG_INIT; \ + \ + if (argc == 1) \ + return 1; + +#define TEST(name) \ + else if (!strcmp(argv[1], name)) + + +#define TESTS_END \ + else \ + return 1; \ + \ + return 0; \ +} + + +#endif diff --git a/tests/test.sh b/tests/test.sh index 77bac66bf..d919ed073 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -1,7 +1,6 @@ #!/bin/bash # Copyright (C) Lumiera.org -# 2007, Christian Thaeter -# Hermann Vosseler +# 2007 - 2008, Christian Thaeter # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as From 7a6b987a5032fb2c96c1b2182212e1ead79cf175 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 11 Jul 2008 05:35:48 +0200 Subject: [PATCH 04/40] merge after integrating the GUI draft; now using multiple build environments and check via pkg-config --- .gitignore | 2 +- SConstruct | 86 ++++++++++++---- admin/scons/Buildhelper.py | 16 ++- admin/scons/LumieraEnvironment.py | 97 ++++++++++++++++++ src/gui/lumiera_ui.rc | 157 ++++++++++++++++++++++++++++++ 5 files changed, 334 insertions(+), 24 deletions(-) create mode 100644 admin/scons/LumieraEnvironment.py create mode 100644 src/gui/lumiera_ui.rc diff --git a/.gitignore b/.gitignore index 5dc912d9e..10dbaee23 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ *.os *.gch ,valgrind.log* -Buildhelper.pyc +admin/scons/*.pyc optcache Makefile.in build/* diff --git a/SConstruct b/SConstruct index 8434b0a98..173803853 100644 --- a/SConstruct +++ b/SConstruct @@ -26,6 +26,8 @@ sys.path.append("./admin/scons") import os from Buildhelper import * +from LumieraEnvironment import * + #-----------------------------------Configuration OPTIONSCACHEFILE = 'optcache' @@ -33,6 +35,7 @@ CUSTOPTIONSFILE = 'custom-options' SRCDIR = 'src' BINDIR = 'bin' TESTDIR = 'tests' +ICONDIR = 'icons' VERSION = '0.1+pre.01' #-----------------------------------Configuration @@ -53,13 +56,13 @@ def setupBasicEnvironment(): EnsureSConsVersion(0,96,90) opts = defineCmdlineOptions() - - env = Environment(options=opts) - + env = LumieraEnvironment(options=opts) + env.Append ( CCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honor CFLAGS, only CCFLAGS env.Replace( VERSION=VERSION , SRCDIR=SRCDIR , BINDIR=BINDIR + , ICONDIR=ICONDIR , CPPPATH=["#"+SRCDIR] # used to find includes, "#" means always absolute to build-root , CPPDEFINES=['-DLUMIERA_VERSION='+VERSION ] # note: it's a list to append further defines , CCFLAGS='-Wall ' # -fdiagnostics-show-option @@ -74,7 +77,7 @@ def setupBasicEnvironment(): appendVal(env,'ARCHFLAGS', 'CCFLAGS') # for both C and C++ appendVal(env,'OPTIMIZE', 'CCFLAGS', val=' -O3') appendVal(env,'DEBUG', 'CCFLAGS', val=' -ggdb') - + prepareOptionsHelp(opts,env) opts.Save(OPTIONSCACHEFILE, env) return env @@ -127,7 +130,7 @@ def defineCmdlineOptions(): ,PathOption('SRCTAR', 'Create source tarball prior to compiling', '..', PathOption.PathAccept) ,PathOption('DOCTAR', 'Create tarball with dev documentaionl', '..', PathOption.PathAccept) ) - + return opts @@ -161,38 +164,42 @@ def configurePlatform(env): setup platform specific options. Abort build in case of failure. """ - conf = Configure(env) - # run all configuration checks in the current env + conf = env.Configure() + # run all configuration checks in the given env + + # Perform checks for prerequisites -------------------------------------------- + if not conf.TryAction('pkg-config --version > $TARGET')[0]: + print 'We need pkg-config for including library configurations, exiting.' + Exit(1) - # Checks for prerequisites ------------ if not conf.CheckLibWithHeader('m', 'math.h','C'): print 'Did not find math.h / libm, exiting.' Exit(1) - + if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'): print 'Functions for runtime dynamic loading not available, exiting.' Exit(1) - + if not conf.CheckLibWithHeader('nobugmt', 'nobug.h', 'C'): print 'Did not find NoBug [http://www.pipapo.org/pipawiki/NoBug], exiting.' Exit(1) - + if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'): print 'Did not find the pthread lib or pthread.h, exiting.' else: conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD') conf.env.Append(CCFLAGS = ' -pthread') - + if conf.CheckCHeader('execinfo.h'): conf.env.Append(CPPFLAGS = ' -DHAS_EXECINFO_H') - + if conf.CheckCHeader('valgrind/valgrind.h'): conf.env.Append(CPPFLAGS = ' -DHAS_VALGRIND_VALGIND_H') if not conf.CheckCXXHeader('tr1/memory'): print 'We rely on the std::tr1 proposed standard extension for shared_ptr.' Exit(1) - + if not conf.CheckCXXHeader('boost/config.hpp'): print 'We need the C++ boost-lib.' Exit(1) @@ -206,8 +213,36 @@ def configurePlatform(env): if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'): print 'We need the boost regular expression lib (incl. binary lib for linking).' Exit(1) - - + +# if not conf.CheckLibWithHeader('gavl', ['gavlconfig.h', 'gavl/gavl.h'], 'C'): + + if not conf.CheckPkgConfig('gavl', 1.0): + print 'Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html], exiting.' + Exit(1) + else: + conf.env.mergeConf('gavl') + + if not conf.CheckPkgConfig('gtkmm-2.4', 2.8): + print 'Unable to configure GTK--, exiting.' + Exit(1) + + if not conf.CheckPkgConfig('cairomm-1.0', 0.6): + print 'Unable to configure Cairo--, exiting.' + Exit(1) + + if not conf.CheckPkgConfig('gdl-1.0', '0.6.1'): + print 'Unable to configure the GNOME DevTool Library, exiting.' + Exit(1) + + if not conf.CheckPkgConfig('xv'): Exit(1) +# if not conf.CheckPkgConfig('xext'): Exit(1) +# if not conf.CheckPkgConfig('sm'): Exit(1) +# +# obviously not needed? + + print "** Gathered Library Info: %s" % conf.env.libInfo.keys() + + # create new env containing the finished configuration return conf.Finish() @@ -221,7 +256,7 @@ def definePackagingTargets(env, artifacts): artifacts['src.tar'] = t env.Alias('src.tar', t) env.Alias('tar', t) - + t = Tarball(env,location='$DOCTAR',suffix='-doc',dirs='admin doc wiki uml tests') artifacts['doc.tar'] = t env.Alias('doc.tar', t) @@ -240,7 +275,7 @@ def defineBuildTargets(env, artifacts): objlib = ( srcSubtree(env,'$SRCDIR/common') + srcSubtree(env,'$SRCDIR/lib') ) - plugobj = srcSubtree(env,'$SRCDIR/plugin', isShared=True) + objplug = srcSubtree(env,'$SRCDIR/plugin', isShared=True) core = ( env.StaticLibrary('$BINDIR/lumiback.la', objback) + env.StaticLibrary('$BINDIR/lumiproc.la', objproc) + env.StaticLibrary('$BINDIR/lumi.la', objlib) @@ -253,7 +288,16 @@ def defineBuildTargets(env, artifacts): env.Depends(objlib, precomp) artifacts['lumiera'] = env.Program('$BINDIR/lumiera', ['$SRCDIR/main.cpp']+ core ) - artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', plugobj) + artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', objplug) + + # the Lumiera GTK GUI + envgtk = env.Clone().mergeConf(['gtkmm-2.4','cairomm-1.0','gdl-1.0','xv','xext','sm']) + objgui = srcSubtree(envgtk,'$SRCDIR/gui') + + artifacts['lumigui'] = ( envgtk.Program('$BINDIR/lumigui', objgui + core) + + env.Install('$BINDIR', env.Glob('$ICONDIR/*.png')) + + env.Install('$BINDIR', env.Glob('$SRCDIR/gui/*.rc')) + ) # call subdir SConscript(s) for independent components SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts core') @@ -269,13 +313,13 @@ def definePostBuildTargets(env, artifacts): il = env.Alias('install-lib', '$DESTDIR/lib') env.Alias('install', [ib, il]) - build = env.Alias('build', artifacts['lumiera']+artifacts['plugins']+artifacts['tools']) + build = env.Alias('build', artifacts['lumiera']+artifacts['lumigui']+artifacts['plugins']+artifacts['tools']) allbu = env.Alias('allbuild', build+artifacts['testsuite']) env.Default('build') # additional files to be cleaned when cleaning 'build' env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ]) env.Clean ('build', [ '$SRCDIR/pre.gch' ]) - + # Doxygen documentation # Note: at the moment we only depend on Doxyfile # obviousely, we should depend on all sourcefiles diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py index ae3f5fb1f..02f17b2b4 100644 --- a/admin/scons/Buildhelper.py +++ b/admin/scons/Buildhelper.py @@ -90,6 +90,18 @@ def globRootdirs(roots): +def filterNodes(nlist, removeName=None): + """ filter out scons build nodes using the given criteria. + removeName: if set, remove all nodes with this srcname + """ + if removeName: + predicate = lambda n : not fnmatch.fnmatch(os.path.basename(str(n[0])), removeName) + else: + predicate = lambda n : True; + + return filter(predicate, nlist) + + def RegisterPrecompiledHeader_Builder(env): """ Registeres an Custom Builder for generating a precompiled Header. Note you should define a dependency to the PCH file @@ -111,7 +123,7 @@ def RegisterPrecompiledHeader_Builder(env): def Tarball(env,location,dirs,suffix=''): """ Custom Command: create Tarball of some subdirs - location: where to create the tar (optionally incl. filename.tar.gz) + location: where to create the tar (may optionally include filename.tar.gz) suffix: (optional) suffix to include in the tar name dirs: directories to include in the tar @@ -132,7 +144,7 @@ def Tarball(env,location,dirs,suffix=''): def createTarball(target,source,env): - """ helper, builds the tar using the python2.3 tarfil lib. + """ helper, builds the tar using the python2.3 tarfile lib. This allows us to prefix all paths, thus moving the tree into a virtual subdirectory containing the Version number, as needed by common packaging systems. diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py new file mode 100644 index 000000000..82d419a65 --- /dev/null +++ b/admin/scons/LumieraEnvironment.py @@ -0,0 +1,97 @@ +# -*- python -*- +## +## LumieraEnvironment.py - custom SCons Environment +## + +# Copyright (C) Lumiera.org +# 2008, Hermann Vosseler +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +##################################################################### + + +from SCons.SConf import SConf +from SCons.Environment import Environment + +from Buildhelper import * + + + +class LumieraEnvironment(Environment): + """ Custom SCons build environment for Lumiera + This allows us to carry structured config data without + using global vars. Idea inspired by Ardour. + """ + def __init__(self,*args,**kw): + Environment.__init__ (self,*args,**kw) + self.libInfo = {} + + def Configure (self, *args, **kw): + kw['env'] = self + return apply(LumieraConfigContext, args, kw) + + def mergeConf (self,other): + """ extract the library/compiler flags from other Environment. + Optionally accepts a list or just sting(s) representing keys + in our own libInfo Dictionary + """ + if isinstance(other, list): + for elm in other: + self.mergeConf(elm) + elif isinstance(other, str): + if other in self.libInfo: + self.mergeConf(self.libInfo[other]) + else: + self.Append (LIBS = other.get ('LIBS',[])) + self.Append (LIBPATH = other.get ('LIBPATH', [])) + self.Append (CPPPATH = other.get('CPPPATH', [])) + self.Append (LINKFLAGS = other.get('LINKFLAGS', [])) + + return self + + + def addLibInfo (self, libID, minVersion=0): + """ use pkg-config to create an Environment describing the lib. + Don't add this defs to the current Environment, rather store + them in the libInfo Dictionary. + """ + minVersion = str(minVersion) + if 0 != os.system('pkg-config --print-errors --exists "%s >= %s"' % (libID,minVersion)): + print "Problems configuring the Library %s (>= %s)" % (libID,minVersion) + return False + + self.libInfo[libID] = libInfo = LumieraEnvironment() + libInfo.ParseConfig ('pkg-config --cflags --libs '+ libID ) + return libInfo + + def Glob (self, pattern): + """ temporary workaround; newer versions of SCons provide this as a global function + """ + pattern = self.subst(pattern) + return glob.glob(pattern) + + +class LumieraConfigContext(SConf): + """ Extends the SCons Configure context with some convenience methods + """ + def __init__(self, *args,**kw): + SConf.__init__(self,*args,**kw) + + def CheckPkgConfig (self, libID, minVersion=0): + print "Checking for library configuration: %s " % libID + # self.Message(self,"Checking for library configuration: %s " % libID) + return self.env.addLibInfo (libID, minVersion) + + diff --git a/src/gui/lumiera_ui.rc b/src/gui/lumiera_ui.rc new file mode 100644 index 000000000..428b7bed4 --- /dev/null +++ b/src/gui/lumiera_ui.rc @@ -0,0 +1,157 @@ +# +# This is the GTK style file for Lumiera +# +# + +#---------- Styles for Standard Widgets ----------# + +style "medium_text" +{ + font_name = "sans 10" +} + +style "medium_bold_entry" = "medium_text" +{ + fg[NORMAL] = { 0.70, 0.70, 0.70 } + fg[ACTIVE] = { 0.70, 0.70, 0.70 } + + bg[NORMAL] = { 0.35, 0.35, 0.40 } + + base[NORMAL] = { 0, 0, 0 } + base[ACTIVE] = { 0, 0, 0 } + base[SELECTED] = { 0, 0, 0 } +} + +style "default_base" = "medium_text" +{ + GtkWidget::cursor_color = {1.0, 1.0, 1.0 } + GtkButton::default_border = { 0, 0, 0, 0 } + GtkButton::default_outside_border = { 0, 0, 0, 0 } + GtkTreeView::vertical-padding = 0 + GtkTreeView::horizontal-padding = 0 + GtkTreeView::even-row-color = { 0, 0, 0 } + GtkTreeView::odd-row-color = { 0, 0, 0 } + + fg[NORMAL] = { 0.80, 0.80, 0.80 } + fg[ACTIVE] = { 0.80, 0.80, 0.80 } + fg[PRELIGHT] = { 1.0, 1.0, 1.0 } + fg[INSENSITIVE] = { 0.80, 0.80, 0.80 } + fg[SELECTED] = { 0.80, 0.80, 0.80 } + + bg[NORMAL] = { 0.35, 0.35, 0.40 } + bg[ACTIVE] = { 0.35, 0.35, 0.40 } + bg[PRELIGHT] = "#565690" + bg[INSENSITIVE] = { 0.10, 0.10, 0.10 } + bg[SELECTED] = { 0, 0.40, 0.60 } + + text[NORMAL] = { 0.80, 0.80, 0.80 } + text[ACTIVE] = { 0.80, 0.80, 0.80 } + text[PRELIGHT] = { 0.80, 0.80, 0.80 } + text[INSENSITIVE] = { 0.80, 0.80, 0.80} + text[SELECTED] = { 1.0, 1.0, 1.0 } + + base[ACTIVE] = "#272a2f" + base[NORMAL] = "#1a1e20" + base[PRELIGHT] = { 0.20, 0.20, 0.20 } + base[INSENSITIVE] = "#4c5159" + base[SELECTED] = { 0.25, 0.25, 0.25 } + + engine "clearlooks" + { + menubarstyle = 2 # 0 = flat, 1 = sunken, 2 = flat gradient +# menuitemstyle = 1 # 0 = flat, 1 = 3d-ish (gradient), 2 = 3d-ish (button) +# listviewitemstyle = 1 # 0 = flat, 1 = 3d-ish (gradient) +# progressbarstyle = 1 # 0 = candy bar, 1 = fancy candy bar, 2 = flat + } +} + +style "default_buttons_menus" +{ + font_name = "sans 10" + fg[ACTIVE] = { 1.0, 1.0, 1.0 } + + bg[NORMAL] = { 0.30, 0.30, 0.35 } + bg[ACTIVE] = "#565690" + bg[PRELIGHT] = { 0.15, 0.15, 0.20 } + bg[INSENSITIVE] = { 0.15, 0.15, 0.20 } + bg[SELECTED] = { 0.15, 0.15, 0.20 } +} + +style "lumiera_adjusters" = "default_buttons_menus" +{ + fg[NORMAL] = { 0.80, 0.80, 0.80 } + fg[ACTIVE] = { 0.80, 0.80, 0.80 } + fg[PRELIGHT] = { 1.0, 1.0, 1.0 } + fg[INSENSITIVE] = { 0.80, 0.80, 0.80 } + fg[SELECTED] = { 0.80, 0.80, 0.80 } + + bg[NORMAL] = { 0.35, 0.35, 0.40 } + bg[ACTIVE] = { 0.35, 0.35, 0.40 } + bg[PRELIGHT] = "#565690" + bg[INSENSITIVE] = { 0.10, 0.10, 0.10 } + bg[SELECTED] = { 0, 0.40, 0.60 } +} + +style "lumiera_progressbars" = "default_buttons_menus" +{ + bg[NORMAL] = { 0, 0, 0 } + bg[PRELIGHT] = { 0.00, 0.36, 0.40 } +} + +style "lumiera_button" = "default_buttons_menus" +{ + xthickness = 1 + ythickness = 1 +} + +style "tearoff_arrow" = "medium_bold_entry" +{ + fg[NORMAL] = { 0.80, 0.80, 0.80 } + fg[PRELIGHT] = { 0.80, 0.80, 0.80 } + bg[NORMAL] = { 0.80, 0.80, 0.80 } + bg[PRELIGHT] = { 0.80, 0.80, 0.80 } +} + +class "GtkWidget" style:highest "default_base" +class "GtkScrollbar" style:highest "lumiera_adjusters" +class "GtkLabel" style:highest "default_buttons_menus" +class "GtkButton" style:highest "lumiera_button" +class "GtkArrow" style:highest "tearoff_arrow" +class "GtkProgressBar" style:highest "lumiera_progressbars" + +#---------- Styles for Lumiera Widgets ----------# + +style "timeline_body" +{ + gtkmm__CustomObject_TimelineBody::background = "#7E838B" +} + +style "timeline_ruler" = "default_base" +{ + font_name = "sans 8" + gtkmm__CustomObject_TimelineRuler::height = 18 + gtkmm__CustomObject_TimelineRuler::major_tick_height = 18 + gtkmm__CustomObject_TimelineRuler::minor_long_tick_height = 6 + gtkmm__CustomObject_TimelineRuler::minor_short_tick_height = 3 + gtkmm__CustomObject_TimelineRuler::annotation_horz_margin = 3 + gtkmm__CustomObject_TimelineRuler::annotation_vert_margin = 0 + gtkmm__CustomObject_TimelineRuler::min_division_width = 100 + gtkmm__CustomObject_TimelineRuler::mouse_chevron_size = 5 +} + +style "timeline_header_base" = "default_base" +{ +# fg[NORMAL] = { 0.77, 0.77, 0.72 } +# bg[NORMAL] = { 0.18, 0.19, 0.22 } +# bg[ACTIVE] = { 0.20, 0.20, 0.20 } +# bg[PRELIGHT] = { 0.20, 0.20, 0.20 } +# bg[INSENSITIVE] = { 0.20, 0.20, 0.20 } +# bg[SELECTED] = { 0.20, 0.20, 0.20 } +} + +class "gtkmm__CustomObject_TimelineBody" style:highest "timeline_body" +class "gtkmm__CustomObject_TimelineRuler" style:highest "timeline_ruler" + +widget "*TimelineHeaderBaseUnselected" style:highest "timeline_header_base" + + From 2dab8cfe6e3117c5141cd3b174037968027878df Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 11 Jul 2008 19:40:11 +0200 Subject: [PATCH 05/40] WIP adapt the nodeoperation to this (possible) solution --- src/proc/engine/nodeoperation.hpp | 200 +++++++++++++++++++-------- src/proc/engine/nodewiringconfig.hpp | 61 ++++---- src/proc/engine/procnode.hpp | 6 +- 3 files changed, 180 insertions(+), 87 deletions(-) diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index 8f3711dc8..2cd5eb09d 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -63,16 +63,6 @@ namespace engine { - /** - * (abstract) base class of all concrete StateAdapter types. - * Defines the skeleton for the node operation/calculation - */ - class InvocationStateBase - : public State - { - - }; - /** * Adapter to shield the ProcNode from the actual buffer management, * allowing the processing function within ProcNode to use logical @@ -80,15 +70,28 @@ namespace engine { * call, using setup/wiring data preconfigured by the builder. * Its job is to provide the actual implementation of the Cache * push / fetch and recursive downcall to render the source frames. + * + * \par assembling the implementation + * This is the abstract base class of all concrete StateAdapter types. + * Each ProcNode#pull() call creates such a StateAdapter on the stack, + * with a concrete type according to the WiringDescriptor of the node + * to pull. The concrete type is assembled by a chain of policy templates + * on top of InvocationStateBase. For each of the possible configuratons + * we define such a chain (see bottom of this header). The WiringFactory + * defined in nodewiring.cpp actually drives the instantiation of all + * those possible combinations */ - class StateAdapter + class InvocationStateBase : public State { - State& parent_; - State& current_; WiringDescriptor const& wiring_; protected: + State& parent_; + State& current_; + + uint requiredOutputNr; + StateAdapter (State& callingProcess, WiringDescriptor const&) : parent_ (callingProcess), current_(callingProcess.getCurrentImplementation()) @@ -99,22 +102,17 @@ namespace engine { virtual State& getCurrentImplementation () { return current_; } - /** contains the details of Cache query and recursive calls - * to the predecessor node(s), eventually followed by the - * ProcNode::process() callback - */ - BuffHandle retrieve (uint requiredOutputNr) - { - return retrieveResult (requiredOutputNr); - } }; template struct QueryCache : NEXT { - BuffHandle step () // brauche: current state + BuffHandle + step () { - BuffHandle fetched = current_.fetch (genFrameID (requiredOutputNr)); + BuffHandle fetched = this->current_.fetch ( + this->genFrameID ( + this->requiredOutputNr)); if (fetched) return fetched; else @@ -126,12 +124,14 @@ namespace engine { template struct PullInput : NEXT { - BuffHandle step () + BuffHandle + step () { this->createBuffTable(); ASSERT (this->buffTab); ASSERT (0 < this->buffTabSize()); + ASSERT (this->nrO == this->nrI ); ASSERT (this->nrO+this->nrI <= this->buffTabSize()); ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]); BuffHandle *inH = this->buffTab->inHandles; @@ -147,10 +147,40 @@ namespace engine { } }; + template - struct AllocOutputFromCache + struct ReadSource : NEXT { - BuffHandle step () + BuffHandle + step () + { + this->createBuffTable(); + + ASSERT (this->buffTab); + ASSERT (0 < this->buffTabSize()); + ASSERT (this->nrO+this->nrI <= this->buffTabSize()); + ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]); + BuffHandle *inH = this->buffTab->inHandles; + BuffHandle *outH = this->buffTab->handles; + BuffHandle::PBuff *inBuff = this->buffTab->inBuffs; + BuffHandle::PBuff *outBuff = this->buffTab->buffers; + + for (uint i = 0; i < this->nrI; ++i ) + { + inBuff[i] = outBuff[i] = + *(inH[i] = outH[i] = this->getSource(i)); // TODO: how to access source nodes??? + // now Input #i is ready... + } + return NEXT::step(); + } + }; + + + template + struct AllocOutput + { + BuffHandle + step () { ASSERT (this->buffTab); ASSERT (this->nrO < this->buffTabSize()); @@ -160,17 +190,45 @@ namespace engine { for (uint i = 0; i < this->nrO; ++i ) { outBuff[i] = - *(outH[i] = this->allocateBuffer(i)); + *(outH[i] = allocateBuffer (i)); // now Output buffer for channel #i is available... } return NEXT::step(); } + + private: + BuffHandle const& + allocateBuffer (uint outCh) + { + BUFFSRC::getBufferProvider(this).allocateBuffer( + this->getBufferDescriptor(outCh)); + } }; + + enum OutBuffProvider { PARENT, CACHE }; + + template + struct OutBuffSource ; + + template<> + struct OutBuffSource + { + State& getBufferProvider (InvocationStateBase& thisState) { return thisState.parent_; } + }; + + template<> + struct OutBuffSource + { + State& getBufferProvider (InvocationStateBase& thisState) { return thisState.current_; } + }; + + template struct ProcessData { - BuffHandle step () + BuffHandle + step () { ASSERT (this->buffTab); ASSERT (this->nrO+this->nrI <= this->buffTabSize()); @@ -186,7 +244,8 @@ namespace engine { template struct FeedCache { - BuffHandle step () + BuffHandle + step () { // declare all Outputs as finished this->current_.isCalculated(this->buffTab->handles, @@ -199,7 +258,8 @@ namespace engine { template struct ReleaseBuffers { - BuffHandle step () + BuffHandle + step () { // all buffers besides the required Output no longer needed this->current_.releaseBuffers(this->buffTab->handles, @@ -238,53 +298,79 @@ namespace engine { /* === declare the possible Assembly of these elementary steps === */ template - struct Strategy - { - BuffHandle step () { NOTREACHED; } - }; + struct Strategy ; + - template<> - struct Strategy< Config > + template + struct SelectBuffProvider; + + template<> struct SelectBuffProvider : OutBuffSource { }; + template<> struct SelectBuffProvider : OutBuffSource{ }; + template<> struct SelectBuffProvider : OutBuffSource { }; + template<> struct SelectBuffProvider<> : OutBuffSource{ }; + + + using lumiera::typelist::Config; + + template + struct Strategy< Config > : QueryCache < PullInput< - AllocOutputFromCache< + AllocOutput, ProcessData< FeedCache< ReleaseBuffers< InvocationStateBase > > > > > > { }; - template<> + template struct Strategy< Config > : PullInput< - AllocOutputFromParent< + AllocOutput, ProcessData< ReleaseBuffers< InvocationStateBase > > > > { }; + template<> + struct Strategy< Config<> > + : ReadSource< + ReleaseBuffers< + InvocationStateBase > > + { }; - // At Application startup: build table of all possible operation configs - namespace { + template<> + struct Strategy< Config > : Strategy< Config<> > { }; - bool - determine_if_case_is_possible (Bits& caseFlags) - { - ////////////////////////////////////////////////TODO: Henne oder Ei? - } - - void - build_table_of_possible_configs () - { - registerPossibleCases (&determine_if_case_is_possible); - } - - - using namespace lumiera; - LifecycleHook schedule_ (ON_BASIC_INIT, &build_table_of_possible_configs); + template<> + struct Strategy< Config > + : ReadSource< + AllocOutput, + ProcessData< // wiring_.processFunction is supposed to do just buffer copying here + ReleaseBuffers< + InvocationStateBase > > > > + { }; - } + template + class StateAdapter + : Strategy + { + + protected: + + /** contains the details of Cache query and recursive calls + * to the predecessor node(s), eventually followed by the + * ProcNode::process() callback + */ + BuffHandle retrieve (uint outNr) + { + this->requiredOutputNr = outNr; + return Strategy::step (); + } + + }; + } // namespace engine #endif diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index 4ee451072..aba017fde 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -27,6 +27,7 @@ //#include +#include #include #include #include @@ -59,11 +60,11 @@ namespace lumiera { { typedef NullType Next; }; - template class _P_> + template class _P_> struct Filter,_P_> - : CondNode<_P_, TY, TYPES> + : CondNode<_P_::value, TY, TYPES> { - typedef CondNode<_P_, TY, TYPES> Next; + typedef CondNode<_P_::value, TY, TYPES> Next; }; @@ -131,7 +132,10 @@ namespace lumiera { struct CombineFlags : Combine { }; - + + + using std::max; + template struct Flag { }; template<> struct Flag<0> : NullType { }; @@ -162,7 +166,7 @@ namespace lumiera { }; template - struct DefineConfigFlags; + struct DefineConfigByFlags; template< char f1=0 , char f2=0 @@ -170,7 +174,7 @@ namespace lumiera { , char f4=0 , char f5=0 > - struct DefineConfigFlags< FlagTuple > + struct DefineConfigByFlags< FlagTuple > : Config { }; @@ -218,15 +222,15 @@ namespace lumiera { , TAIL > > { - typedef Node, FLAGS> CurrConfig; - enum{ BITS = max(FlagInfo::BITS, FlagInfo::BITS) + typedef Node, FLAGS> ThisConfig; + enum{ BITS = max (FlagInfo::BITS, FlagInfo::BITS) }; template static FUNC::Ret accept (FUNC& functor) { - functor.template visit(FlagInfo::CODE); + functor.template visit(FlagInfo::CODE); return FlagInfo::accept (functor); } }; @@ -251,16 +255,18 @@ namespace engine { ///////TODO using lumiera::typelist::Config; using lumiera::typelist::FlagInfo; + using lumiera::typelist::DefineConfigByFlags; ///////TODO enum Cases { - CACHING, + CACHING = 1, PROCESS, INPLACE, - NUM_Cases + NOT_SET = 0, + NUM_Cases = INPLACE }; typedef std::bitset Bits; @@ -293,14 +299,14 @@ namespace engine { /** * Helper for fabricating ProcNode Wiring configurations. - * This object builds a table of factories, one for each - * possible node configuration. Provided with the desired - * configuration encoded as bits, the related factory can - * be invoked, thus producing a product object for the - * given configuration. + * This object builds a table of factories, holding one factory + * for each possible node configuration. Provided with the desired + * configuration encoded as bits, the related factory can be invoked, + * thus producing a product object for the given configuration. + * * \par implementation notes * The actual factory class is templated, so it will be defined - * at the use site of ConfigSelecgtor. Moreover, this factory + * at the use site of ConfigSelector. Moreover, this factory * usually expects an ctor argument, which will be fed through * when creating the ConfigSelector instance. This is one of * the reasons why we go through all these complicated factory @@ -310,18 +316,19 @@ namespace engine { * factories, which in turn happens each time we use a new * bulk allocation memory block -- typically for each separate * segment of the Timeline and processing node graph. - * The selection of the possible flag configurations, for which - * Factory instances are created in the table, is governed by - * the type parameter of the ConfigSelector ctor. This type - * parameter needs to be a Typelist of Typelists, each - * representing a flag configuration. The intention is to - * use template metaprogramming to extract all currently defined - * configurations for StateProxy objects to drive this selection. + * + * NOw the selection of the possible flag configurations, for which + * Factory instances are created in the table, is governed by the + * type parameter of the ConfigSelector ctor. This type parameter + * needs to be a Typelist of Typelists, each representing a flag + * configuration. The intention is to to drive this selection by + * the use of template metaprogramming for extracting all + * currently defined StateProxy object configurations. */ template class Factory> class ConfigSelector { - typedef typename Factory::Ret Ret; + typedef typename Factory::Ret Ret; typedef typename Factory::Param Param; struct FacFunctor @@ -342,7 +349,7 @@ namespace engine { typedef boost::scoped_ptr PFunc; typedef std::map ConfigTable; - ConfigTable possibleConfig_; + ConfigTable possibleConfig_; ///< Table of factories /** Helper: a visitor usable with FlagInfo */ struct FactoryTableBuilder @@ -363,7 +370,7 @@ namespace engine { void visit (ulong code) { - typedef typename DefineConfigFlags Config; + typedef typename DefineConfigByFlags Config; factories_[code].reset (new FactoryHolder > (ctor_param_)); } diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp index b41405434..4a3249dc9 100644 --- a/src/proc/engine/procnode.hpp +++ b/src/proc/engine/procnode.hpp @@ -76,7 +76,7 @@ namespace engine { * holding the actual buffer pointers and issuing the recrusive pull() calls * @see NodeWiring#callDown default implementation */ - virtual BufferID callDown (State& currentProcess, BufferID requiredOutputNr) const =0; + virtual BuffHandle callDown (State& currentProcess, uint requiredOutputNr) const =0; friend class ProcNode; @@ -118,8 +118,8 @@ namespace engine { * this node delivers more than one output channel) * @return ID of the result buffer (accessible via currentProcess) */ - BufferID - pull (State& currentProcess, BufferID requiredOutputNr=0) const + BuffHandle + pull (State& currentProcess, uint requiredOutputNr=0) const { return this->wiringConfig_.callDown (currentProcess, requiredOutputNr); } From cb5d1fd03beec4010527c18680ee5827abfbd13d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 14 Jul 2008 03:34:18 +0200 Subject: [PATCH 06/40] WIP further refactored the nodeoperation --- doc/devel/uml/class132741.html | 2 +- doc/devel/uml/class142469.html | 5 +- doc/devel/uml/class142725.html | 4 + doc/devel/uml/class143365.html | 10 +- doc/devel/uml/class143493.html | 9 +- doc/devel/uml/class143621.html | 7 +- doc/devel/uml/class143749.html | 10 +- doc/devel/uml/class143877.html | 7 +- doc/devel/uml/class144005.html | 6 +- doc/devel/uml/class144261.html | 25 +++ doc/devel/uml/class144389.html | 24 +++ doc/devel/uml/class144517.html | 25 +++ doc/devel/uml/class144645.html | 23 ++ doc/devel/uml/classes.html | 14 +- doc/devel/uml/classes_list.html | 14 +- doc/devel/uml/fig131973.png | Bin 24696 -> 24292 bytes doc/devel/uml/fig132357.png | Bin 18575 -> 25627 bytes doc/devel/uml/index.html | 16 +- doc/devel/uml/index_60.html | 6 +- doc/devel/uml/index_65.html | 6 +- doc/devel/uml/index_66.html | 3 +- doc/devel/uml/index_67.html | 43 ++-- doc/devel/uml/index_69.html | 4 +- doc/devel/uml/index_70.html | 1 + doc/devel/uml/index_72.html | 2 +- doc/devel/uml/index_73.html | 7 +- doc/devel/uml/index_77.html | 2 +- doc/devel/uml/index_79.html | 1 + doc/devel/uml/index_80.html | 3 +- doc/devel/uml/index_81.html | 1 + doc/devel/uml/index_82.html | 3 +- doc/devel/uml/index_83.html | 5 +- doc/devel/uml/index_84.html | 4 +- doc/devel/uml/index_86.html | 10 +- doc/devel/uml/index_87.html | 2 - doc/devel/uml/public_operations.html | 6 +- src/common/util.hpp | 4 + src/proc/engine/nodeoperation.hpp | 142 +++++++------ src/proc/engine/nodewiringconfig.hpp | 54 +++-- src/proc/state.hpp | 4 +- uml/lumiera/128389 | 301 ++++++++++++++++++++++----- uml/lumiera/128901 | 2 +- uml/lumiera/129285 | 2 +- uml/lumiera/131973.diagram | 24 ++- uml/lumiera/132357.diagram | 201 +++++++++++++----- uml/lumiera/5.session | 22 +- uml/lumiera/lumiera.prj | 2 +- wiki/renderengine.html | 78 +++---- 48 files changed, 794 insertions(+), 352 deletions(-) create mode 100644 doc/devel/uml/class144261.html create mode 100644 doc/devel/uml/class144389.html create mode 100644 doc/devel/uml/class144517.html create mode 100644 doc/devel/uml/class144645.html diff --git a/doc/devel/uml/class132741.html b/doc/devel/uml/class132741.html index a9dd11ca8..89bb5e220 100644 --- a/doc/devel/uml/class132741.html +++ b/doc/devel/uml/class132741.html @@ -16,7 +16,7 @@ -

Declaration :

  • C++ : class State
  • Java : public interface State

Directly inherited by : InvocationStateBase StateAdapter StateProxy

+

Declaration :

  • C++ : class State
  • Java : public interface State

Directly inherited by : StateAdapter StateProxy

Artifact : stateproxy, Component(s) : Builder

Relation currFrame (<unidirectional association>)

Declaration :

  • Uml : # currFrame : Frame
  • C++ : protected: Frame * currFrame
diff --git a/doc/devel/uml/class142469.html b/doc/devel/uml/class142469.html index 31b631174..58c82f008 100644 --- a/doc/devel/uml/class142469.html +++ b/doc/devel/uml/class142469.html @@ -16,9 +16,8 @@ -

Declaration :

  • C++ : template<class WIRING, class POLICY> class StateProxy : public State, public InPlace
- -
Relation wiring (<unidirectional association>)

Declaration :

+

Declaration :

  • C++ : class StateProxy : public State
+

All public operations : allocateBuffer , fetch , isCalculated , releaseBuffer

diff --git a/doc/devel/uml/class142725.html b/doc/devel/uml/class142725.html index 13bdadf5b..93345b68f 100644 --- a/doc/devel/uml/class142725.html +++ b/doc/devel/uml/class142725.html @@ -17,5 +17,9 @@

Declaration :

  • C++ : class WiringDescriptor

Directly inherited by : NodeWiring

+
+ +
Operation callDown

Declaration :

  • Uml : + callDown(inout : State, in requiredOutput : uint) : , exceptions : BuffHandle
  • C++ : public: virtual callDown () throw (BuffHandle)
+

All public operations : callDown

diff --git a/doc/devel/uml/class143365.html b/doc/devel/uml/class143365.html index 8f96f6b27..c4e3718c3 100644 --- a/doc/devel/uml/class143365.html +++ b/doc/devel/uml/class143365.html @@ -16,7 +16,13 @@ -

Declaration :

-
+

Declaration :

+ +
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+
Relation <unidirectional association>

Declaration :

+
Operation callDown

Declaration :

  • Uml : + callDown() :
  • C++ : public: callDown ()
+

All public operations : callDown , callDown

diff --git a/doc/devel/uml/class143493.html b/doc/devel/uml/class143493.html index 63ef30fb2..37a6496d9 100644 --- a/doc/devel/uml/class143493.html +++ b/doc/devel/uml/class143493.html @@ -4,21 +4,18 @@ -Class InvocationStateBase +Class OperationBase -
Class InvocationStateBase
+
Class OperationBase

-

Declaration :

  • C++ : class InvocationStateBase : public State

Directly inherited by : Caching

-
-
-

All public operations : allocateBuffer , fetch , isCalculated , releaseBuffer

+

Declaration :

  • C++ : class OperationBase

Directly inherited by : FeedCache ProcessData ReadSource

diff --git a/doc/devel/uml/class143621.html b/doc/devel/uml/class143621.html index 39379446c..649d441e7 100644 --- a/doc/devel/uml/class143621.html +++ b/doc/devel/uml/class143621.html @@ -4,21 +4,20 @@ -Class Caching +Class QueryCache -
Class Caching
+
Class QueryCache

-

Declaration :

Directly inherited by : Process

+

Declaration :

Directly inherited by : Strategy

-

All public operations : allocateBuffer , fetch , isCalculated , releaseBuffer

diff --git a/doc/devel/uml/class143749.html b/doc/devel/uml/class143749.html index 2d3726110..426d12aaa 100644 --- a/doc/devel/uml/class143749.html +++ b/doc/devel/uml/class143749.html @@ -4,21 +4,21 @@ -Class Process +Class PullInput -
Class Process
+
Class PullInput

-

Declaration :

  • C++ : class Process : public Caching

Directly inherited by : InPlace

+

Declaration :

Directly inherited by : QueryCache Strategy

-
-

All public operations : allocateBuffer , fetch , isCalculated , releaseBuffer

+ +
Relation <association>

Declaration :

diff --git a/doc/devel/uml/class143877.html b/doc/devel/uml/class143877.html index b3164a70a..bad34df65 100644 --- a/doc/devel/uml/class143877.html +++ b/doc/devel/uml/class143877.html @@ -4,21 +4,20 @@ -Class InPlace +Class ReadSource -
Class InPlace
+
Class ReadSource

-

Declaration :

  • C++ : class InPlace : public Process

Directly inherited by : StateProxy

+

Declaration :

Directly inherited by : Strategy

-

All public operations : allocateBuffer , fetch , isCalculated , releaseBuffer

diff --git a/doc/devel/uml/class144005.html b/doc/devel/uml/class144005.html index 9fd2629bc..bc2bf60f2 100644 --- a/doc/devel/uml/class144005.html +++ b/doc/devel/uml/class144005.html @@ -4,19 +4,19 @@ -Class WiringTable +Class BuffTable -
Class WiringTable
+
Class BuffTable

-

Declaration :

  • C++ : template<int SIZ> class WiringTable
+

Declaration :

  • C++ : template<int SIZ> class BuffTable
Relation <directional aggregation by value>

Declaration :

Relation <unidirectional association>

Declaration :

diff --git a/doc/devel/uml/class144261.html b/doc/devel/uml/class144261.html new file mode 100644 index 000000000..d9c258fab --- /dev/null +++ b/doc/devel/uml/class144261.html @@ -0,0 +1,25 @@ + + + + + + +Class Invocation + + + + + +
Class Invocation
+

+ + + + +

Declaration :

  • C++ : class Invocation
+ +
Relation <unidirectional association>

Declaration :

+
Relation <unidirectional association>

Declaration :

+
Relation <unidirectional association>

Declaration :

+ + diff --git a/doc/devel/uml/class144389.html b/doc/devel/uml/class144389.html new file mode 100644 index 000000000..fb3f25daf --- /dev/null +++ b/doc/devel/uml/class144389.html @@ -0,0 +1,24 @@ + + + + + + +Class ProcessData + + + + + +
Class ProcessData
+

+ + + + +

Declaration :

Directly inherited by : PullInput

+
+ +
Relation <association>

Declaration :

+ + diff --git a/doc/devel/uml/class144517.html b/doc/devel/uml/class144517.html new file mode 100644 index 000000000..8eace63da --- /dev/null +++ b/doc/devel/uml/class144517.html @@ -0,0 +1,25 @@ + + + + + + +Class Strategy + + + + + +
Class Strategy
+

+ + + + +

Declaration :

+ +
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+ + diff --git a/doc/devel/uml/class144645.html b/doc/devel/uml/class144645.html new file mode 100644 index 000000000..c5cc7c0cb --- /dev/null +++ b/doc/devel/uml/class144645.html @@ -0,0 +1,23 @@ + + + + + + +Class FeedCache + + + + + +
Class FeedCache
+

+ + + + +

Declaration :

Directly inherited by : ProcessData

+
+
+ + diff --git a/doc/devel/uml/classes.html b/doc/devel/uml/classes.html index 7992c89cf..319577971 100644 --- a/doc/devel/uml/classes.html +++ b/doc/devel/uml/classes.html @@ -27,11 +27,11 @@ AutoAutomation data for some parameter (i.e. a time varying function) BackendCache BuffHandle +BuffTable Buildableinterface BuilderFacadeboundaryProvides unified access to the builder functionality. While individual components of the builder subsystem may be called if necessary or suitable, it is usually better to do all extern invocations via the high level methods of this Facade BuilderToolinterfaceUsed according to the visitor pattern: each Tool contains the concrete implementation for one task to be done to the various MObject classes BuildInstruct(Interface) building instructions to be executed by the Builder on the render node network under construction. -Caching Caller Categorytree like classification of Assets Clipbookkeeping (asset) view of a media clip. @@ -60,6 +60,7 @@ ExplicitPlacementinterface External Factorya template for generating functor-like Factory objects, used to encapsulate object creation and providing access via smart-pointers only. +FeedCache File FileHandle FileHandleCache @@ -74,10 +75,9 @@ FrameDescriptorinterfaceA FrameDescriptor implements the higher level interfaces for frames. Further refinements are made by subclassing and policy classes FrameReference GLBuf -InPlace InterpolatorProvides the implementation for getting the acutal value of a time varying or automated effect/plugin parameter Invalid -InvocationStateBase +Invocation Label Link LocatingPinAn element with value semantics, which actually implements the placement of some MObject by positioning it in some way. @@ -94,6 +94,7 @@ MutexI provided a reworked Mutex class in my Cinelerra2 repository NodeCreatorToolThis Tool implementation plays the central role in the buld process: given a MObject from Session, it is able to attach ProcNodes to the render engine under construction such as to reflect the properties of the MObject in the actual render. NodeWiring +OperationBase ParameterDescriptor and access object for a plugin parameter. Parameters may be provided with values from the session, and this values may be automated. ParamProviderinterfaceA facility to get the actual value of a plugin/effect parameter PathManagerWhile building a render engine, this Strategy class decides on the actual render strategy in accordance to the current controller settings (system state) @@ -105,13 +106,16 @@ Previewalternative version of the media data, probably with lower resolution Prockey abstraction: data processing asset ProcDispatcher -Process +ProcessData Processor ProcNodeinterfaceKey abstraction of the Render Engine: A Data processing Node ProcPattspecial type of structural Asset representing information how to build some part of the render engine's processing nodes network. ProjectorSpecial video processing node used to scale and translate image data. +PullInput +QueryCache QueryHandlerinterface QueryHandlerImpl +ReadSource RelativeLocation RelTypeenumthe possible kinds of RelativePlacements RenderEngine @@ -132,6 +136,7 @@ StateAdapter StateProxyimplementation std::exceptionauxiliary +Strategy Structkey abstraction: structural asset ThreadWe can basically reuse the Thread class design from Cinelerra2, Thread becomes a baseclass for all Threads Timedenotes a temporal position (time point), based on timeline start.

investigate posix.4 realtime timers, wrap these here @@ -147,7 +152,6 @@ VFrame Visitable WiringDescriptor -WiringTable Wish WriteBuffer WriteBufferPool diff --git a/doc/devel/uml/classes_list.html b/doc/devel/uml/classes_list.html index f89fb300a..8e8c2d9a6 100644 --- a/doc/devel/uml/classes_list.html +++ b/doc/devel/uml/classes_list.html @@ -28,11 +28,11 @@ Auto
BackendCache
BuffHandle
+BuffTable
Buildable
BuilderFacade
BuilderTool
BuildInstruct
-Caching
Caller
Category
Clip
@@ -61,6 +61,7 @@ ExplicitPlacement
External
Factory
+FeedCache
File
FileHandle
FileHandleCache
@@ -75,10 +76,9 @@ FrameDescriptor
FrameReference
GLBuf
-InPlace
Interpolator
Invalid
-InvocationStateBase
+Invocation
Label
Link
LocatingPin
@@ -95,6 +95,7 @@ Mutex
NodeCreatorTool
NodeWiring
+OperationBase
Parameter
ParamProvider
PathManager
@@ -106,13 +107,16 @@ Preview
Proc
ProcDispatcher
-Process
+ProcessData
Processor
ProcNode
ProcPatt
Projector
+PullInput
+QueryCache
QueryHandler
QueryHandlerImpl
+ReadSource
RelativeLocation
RelType
RenderEngine
@@ -133,6 +137,7 @@ StateAdapter
StateProxy
std::exception
+Strategy
Struct
Thread
Time
@@ -148,7 +153,6 @@ VFrame
Visitable
WiringDescriptor
-WiringTable
Wish
WriteBuffer
WriteBufferPool
diff --git a/doc/devel/uml/fig131973.png b/doc/devel/uml/fig131973.png index 78f068e2d42da80cc06b30cb0e7cca9977571b43..d16d88131e90ff3c19b82c42067cdb5ef8e708d7 100644 GIT binary patch literal 24292 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 literal 24696 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 diff --git a/doc/devel/uml/fig132357.png b/doc/devel/uml/fig132357.png index e454c31e9260ad9bb2f9189843f9759158a0cf96..8f89db240858111252a9d5f3d9d92550d208a503 100644 GIT binary patch literal 25627 zcmb5W1z45q);2nk7D*+fV*-nA5J8ZT4(aZY?oOqVo(KpCNH<7#2@=xXAl=>lKd!a+ z{?^{#KIcE@y}WSdThH^1agY1H#|(WdCyxG@@G%4eL6?*eRfIs`0wIt`<|vQAEB!2W zo)8E*L{d~p+3m~jyr;H|IT_?|F_C%H+mPBRt{TljHjq+UQsIv}tzngN8;V`ku@PDI z4w{;fPimUjJA{^WHR=h`5ja1Y_Ht(X+#g;voDA^Aj&Y8DNxbqF6cBhfz<=^(qmTDw z$36Ar%Lx`#@{usgFhpbo@ij`iuRcF`g4jtFfTi>Q`(a>47TWu0RusQzCv2$vxlK@) zTR;}Br0(yhq2$EWItat<=Y+x`%&epo0h^CzEm0&3ySpVUDERrc%c-$(i^zVzyn_9& zPpn|WJTCU*YU`QR)LaeNQ-Uy3e3!mk53e8W5Y>1%KdzF&ip0a?&^ojCvs=AaQm`lF zvaq+euQQYWgX5bZkz~!gLEYKe!O}v@&w{>w_4Pj|iZ$fqq?vW+IE^K26|5<#sk`Rp z$muPZAyC@ja`o-vej&9Q5#>)Tk}%i(XSUN0oaE&9eTnE9^r8;F;PhWYeShmzLu6Wjgq8>hIuQbA`G7go#a|x+tIOtst4`y)mw~}!h)go~_VroX z6}PhbXLc)fJzQK98l|3rl6Sfb>oj3M?~ozb53ti=tmon467=>f3N1c4ki(-D)P2k< z%gNHQG38o8O$Z8R+AG%*A_$|-OJ07j>YSXJ(5Gx&k#7p~^NTdOVx@Avw%R$A=Zti9 zA(Om3g@>qXB`18!QWDy`I%%K#RqoRy2yt_J^2(mFv)$+q$lGCL2*H!i99asP9GtN1 z0WG8wg-oQ0$+WVXn&nw6mGs{35PlcNddCfnoE#5XJeBGd?CQyA#$Qb-5#%Vajm{Eg zotHtuzBfzldUSNW8w2x(Wn+bfmfAWAN(Z|2PQjW(!@~pp5o>ncPiafmHw5`T&uJja zYLf)dw;n-a^Nsa~5|iJVuD^jy)j4Y9$}w76o)~rc^!7Cp5WYl&$jH<)tN*Gq=^gHh z>aE?1h-mo2|6CfjTIa~KF}V2bspH}8RfHE&d0APb!&=l@uWL9lFF6~V+gvRLDQRI6 zW5JX5dn@QX66QW4L_;GXKjG>M^Jnkk+bT?5ybn)u&EbY?63T99|Gx2nl@X)9Da zm?)ZVa5Be%yx_xha3s22?FrMWAzf_IGqm1o@qI8~n6sJt;}JwjsYxmKG332g|Mg|r z`qt=!&a;zKVho6nk3Frt?#W3Q{Cmk*;$*tjU`*e=xy0BnUi_?fe~vqvP4~QacADOo z;^MWRaU7MEV|tBwR?T_kk|XG10ReCi=VURffeo&%QKYXV-n`L}l(awmrModeee69ZE=8psBfyhUYdhe)n^B?Mn=Her9HIetxy#4^A#F zTrRG-j~|N?IZ|++5$Ni+*U^DR5*R!B#`r1d_jjiHXCuvUcp7xV`(#2TOA*C=-gGS9xnE1| z%0~%#P1pfOqM`^@;TNS80ns4_f5t`K1Mza1fiwy_=Wjj%SQ%*H;wTjLt>=U=Rragb zp1i?FD3T1k2Sl(QyYM}(|4Rr3`x=p=VR7r_1?y3B>5@tkU+DB(Q_#P5>-IlK_}jys zI|5%4$UHf7#ARkk%Jhqo*-y&S_pSCaC{rgB@Pa^K z(7_Zwd;$V>W#!Yljx+&p3+wyo>FJ}RBkq*nwl0H-P>g|3ko!Kbp;$&CE(%V85jz0 zZ@nOw=d-q>BO{Ra?Mnm%1dVP-emt{NQ&VZa_tSlSPaqzrn=zk0b=;-z&o|VFN09~v z1$B0IhK7bhqEl0C%+1Y>jg1Wr%Pl8MXeK|!#YGUXeV>|g`_=lP#&*$$UG`=L?X|iz z&U2ierwbys#>StLlXuqEs@#s2|Ju=yFnswW&d}S7K>|9k>cXhHc*#7Q%XM{vVh@+LgJQq>x$);0wmJE_eB4hjNjChNLTwGNb2a8I* z2MbL~N=j7j+v4=}^kC@mC1J!E8v`kP%H?cEo!X6V!((Ho;9h#wP0!5SgAtue0B18W zFrchz)D?;Tr<{d>yT>g2Hf#P6LhHm(BI{iB&>e+_b~Chyr51LW~`_PtEf2nX3&msBIvLh zlAXOq=6fpyGX{WQF!dEU*B)Py*`3K!NmyThKOzJp*vQ;mGVpUo24%rxn%d6k=|uY0 z7%K6+i9~%u)bE>AnwQo;@!Q;#H8i%iw(@g-Z#?EBSv$m3%c_1gFk;3 zeEs_VT0%qPImBwZJiAABcc!YhqeB!NQ4g$%>oa1E;Ggs~G@Y%j`mOKb7}QF3XKN0= z2co$iELel73N{-XpUXo77!+~x89x5(%*^)A4w&%#KDV_!oHzkT5|!WT!Zah=mz?%y z&d&E{bsAjaoHU?M_`*fey2#Qe%&n~WUH89aXHz3lK^+L9-tJ`OdA)kM&Hr67eQ4m1z6l$WVTAK{RKr8# zdsw(@(oC+KU+J6#d~c67corEIm6g?N?O;Jc+Y`m&B*em`uVxm@GUG#}K5nAzh)Zx* zR&jZG&*%B3rnU=s7bJ7giYYmU$Hy0@>CNTJJNtn}V$sw@^;srYAro(MY)s(_3eB3F zn4qN%F3~$^@i^^&+6y56W*$*Y)no0L&$4&LcPT|r9p}j&CJXDH2X_RR_6?)@4mjUU`%mN zPENU$dR4WW%#4gji-eHw?poG&k%i681Je~e`GX6MR|JsGND`SQVz!Kj5bQg+);5aO znJVO?Bcbc-mr6NB8s)~xVPXB}d(#vYPsJmNH+P5tP8ezSW`O&3b@K6``Q)YVy^FH) zcDad?ks-#hH4nXoO@r^Nyf*;14+EI%aBR$2HCMHx_LQcmq@={&Sx2t8&S@*s?~%W4 z|Dvz&qxS&;?u_x*;0Vfy!ENNV4`7czE1^%-PEN=aV+!l**H?QiAy~LvYSPm4eO8Xv z*0175IsBxCO z`jitrj{OTLr*mO3I9nc(Hdu(!MT~_012zsCPfeSAYGbYQ&e7cs^~A*e%|#lX_R6X; zIz&3bCM)$}X*=bm3Iqa0 z%@jjNct13>2Vmjeo=CM#zlDM1%ofAz;@~qXD4vgx5gEVR`T^OXKI{5`Ga7ozcYW5& z39oBq--CrT&-2xpDRq~<>+#+MTe#MrKUa6BJ8&6Xiu8P6D=4%Lm+5)k^5aQ+pKWWL ztWzT)*`Q#MmgeLrs;WLG=3nYhE~u>J@Vr>Bub=tRiB$Owx_2Y{Rkii1JSG~HW)T|^ zqN1X2dxhs(#IQzA(K39^%l=_NKrm*zct+q63aUJJ$%S-1%BJuCCz#^qRz@6( z*GUECC-w~xMf=-o{|h$#+v*0W=g&jo_r{n3tR=jh!b{7}uINWC#18+rZ`Kd0g<_?= zQ^|ImMi_3XRR0~D?gD;SoI-CsK`7Y_ySX41fqiM+u?)$%B*a&iu*j*S$bUa>Gn?(M zBn<^K_@xE=sTUixr}ac$KwK`!!@v`LSdK#onaHLbeMdY*H_vX=YbgD}nG#AI4xS>w zql=L}cE$2?bE`F$rWE)a%=%}63{6J)9&_ELAHF&7d4ABc7~4I8`>dIbBe~4?p)^f; z>(63U$}>J<031FsyKt6*m)GfRI}wz&m)D(=#yhB}Cxk@P6{dF}Pm{^gNK5wg^feAO zzsY0)(9qP}((>}UzF30;6n_`PnTil{6miFsL?Iy}8X5H_(|%nM5nJl)n4q8q%ZXi+ z3f`)!Gw|L^7vkK4?cM$6@{Hv>5VjoGJIpL1hesUFPQq?Z^Y;f*mY3RFlvJEmLD|gj zwva9DiZFR-%+{HzRo?cESXSDUhJy?_H=O=Jq^YWZ_r*Qoa zq>dY6vB0PqYnz+DF)@j&tAjf$ue}V6iP=x{ZSMP7zq`c7Nv_`$Niw~()S#l0NG5pS zS8y!7DDYs;Ne9!IvwwOWfzPmd_SylD)BrJ`C zHU|`lUY4YS0r5B=`1Cp;MY*)F@}-aEvHBGB#RamXr+3 zmDTFKun}s*K=snY0kgNKC?i90n;Q*OO#R9DZyB7ODjgk-tgZba`Wpi#DKE>4iuiSjMJ(*IxE}tU#@q-eF6Z~1q@Vg!T|xV!)rssLs0Ph z`STnXUunA!q%FEFcZ}cO2EQ9zKzP5sO-Mzh1JP%Xd-CKZS~J50OK|G%Nnm0%x3=1& zj^xkEIi{kZkfRF7hbNta^UoSHTX_Ls5IHVZRx(OTCNeg8qr?Jk9TL*Zwzj8oY0NL2 z=>>VO+|mSI+009=YbmK;`reHqB4WaQb+wHJrJzoOoT%7!K%lIk`!S)Od9tsX9Eff( z4VfHRU|@P;A{-Bay}JhnGDNVYHkP*Z>goqvYnEi>|B6@ttz>k)hy!I|b#*+0+Q86= zqVG|Lq8)lwC??tcOdZF&lXWTq0X77ozT^%2zX!uPwhc1D34U+Q`#RS(w9vO&634<~Vp)-qzgdcpxTAo<0RQOe%S{KIRNv~mxue@2>%bac z-@(5R@vT}r3_Uc)LT=>Hu zH5{fdxDrPuc}k=cA{7)IwNiQKnQwa~y*kbbEuMPiAmdPki|5H6CXz}7fRHK*N!$7$ z3J9?O_1*(Y6{^4}d??mLRCH}XkX(Lou{;P81NX&RHFA~{eITVffN~?5N5$Oy-T8h$ zC=S9R(0>on;jhFNUw;3#&wP_ioK8QRJgL#}tj-VBRrD683KQ(TyrLr`xfj9Nl>P0;c{M!rf5=U#N*hnOegfV`EcrBm^qf{~cKgN1ZWR?J`qc*nyP4Vi|3!b|0 z8T3x7fTUKEUH_S<;8E6-gL&V*tLgZs9%IyhKJQQNvo_y0VmLo=V&OXM2&D=`7e##8A3TVB;_9xS7xw}n*}sO+?885x8ndFzw#)iwcvdO}8^TFSyiQAsPgago*iP7|t z{Mlo-^)q5Vmmm1KhtI}&-k$+9pHWh>wA8)2r|mo%Iln%|*VRoz z$SDj+!?Hn%=>jk<1O+4Y*OEb6f|}dO`pRf78VnYhotwtO;tT(X!-@N8RG#}QYpX|k zO&*iC&r&n4kyuI7C)&c6myK*}4i63JC1IGP{5#{8=W}&sez7Kx<;*4LONHnE=+(b< zY_PK0Z96`$DJz2;&51CqfImIu2gq%r#ugLnb9$`iSN8NITkWyNRHnr5O3I6nPO7oT zjTaX7UB4)YDN!=s$s0l3u8yri8j6mVt+9D!${uIhf6c~dRS&L2PHuw7RzS1fDLFND z0I+CzGO7ITF<-oH&vsn*_eBmCS={fg@4)itiXd{h^N0cb7Z_U-zH&JI5vVw1Xy~o? z{q0#illa5!Nw&VbSOlTl&cq5pg3Qd^jvJkFpMU*Y=l)!(T&UXrtf$$CUpOS^1#x8B zqIz==dw)OrRZivUnRA2d!Qd-T2Wu7O(sj>FvM6r^&IiemLC8epa{bPBBJ@)yHV3A7d*)yuf7CkfT z#CjZ1<46b(!e;1C(-d(-u_6V9Pv0PBp1WsJWWWOsKIu>TW-MM4rcR|fn@YOVT_Z-L zWyb{Atzm3Eh=r#F?BgPDWH#WvGE!(nFuCVe;U=Q6nmCm(Ej*b*?^hc?27r&EsK8q^ zlR{@HLN}!9GNHZ0NH1DYVAy^i#Huqfm;LYlUg1gX+ohrQ{tG}^(omm$Wp4}QK0b@k zYI>QychyY*kPzwCwu^)uhE9{mI*4P<&GvhG?>~L|($|N!*P@dI7|~ViGvg^P%XPs1 z3V0|#PIEW4jg~y7J>cKsqB6xE(hh_#3J*oDrf6 zoScllrDrP6D=K2=;lVmzi36Y?&@VhMU&h_^MZjQci#|Ie#ag-G;G3IcwYB7Jn4~Wb zoBjno1QY7KFhswD6H-u6pgZ#i#o<(=`v<$k&!3%izO_2`#sgX>0i4R#2pj+HEXbcy zl0Cex`>L^Ioa`fr5K!&3em&V>PxwfrofNPVg=IkkFb%DICp$qvdV0ynkL*?DJ@2Zy zMT9=U@kfQM{kS-tnpkJ70muUT>mK?kf8q;8H037$9_8|HtgEs^w;Wnp`{RYK03R|j z6(d02Z3y+R-X0nCkLIq?(9F-2^F9Z0GBilJk|oJpiu0jwBAx@3d1u>q74K{#$!y8+ zxbGZ!2fu86iFYx*IwpDky!`fa|HSvUO9_Omwc$d9r9UPdZ~~~f#6yP25+ZIU1AKIu z0KW-BA7JynLc<$hGfCJYiUt8>FJUY?MbL5dd-Q*~v~}bD6_i0<+q9o0L=ZaNHUIR5 z_@$d&>0ovivji%0W!V!j(mM4{;UAN3eh5=u1C-w({HIEDaI#T4Su($|LjB-toxxYc5g#XYHpTVP{q^zCPF&_yq0Q+Cu3;<`@ID>*FLaQ@YL0U5eQKXnMSb&!?B->3k$wd||SS02t&RaI5174*}f zpn4S*Uq>}Xo}sQEh>u=t*?dhPla(u>YdhwIzln-g|Al_aIo!9J#JMXCX2G9FhtE&e zemZNJmja;KhMmMV_eJ%p$o%UT+eNwsqD@4{RNm1#fYguI_+~V1@_(dgpE$nm!~BT- zXzvjeCbs9|+kyhFVFO~8eh_YpN~jug31CPMT~d@FO#ZK$1l&W;bc1Z9-zWh~*y+O& z3WX>>cq`+BcUj&ZxQOBCkEtY2k~qOa?x72p<#QTExF-XTWAFQPv5C z-@67#;)8Gih330GDspPO24%Q=gZ7UL1oa~7Py|rd5jt?)=k?IG8cJC3DRfft=GQKp z({5z#pne;9({Vd{Avd?s)!7bZR%;3b!0MWH5;)K$mEO+r8oKe@naCH>!@8=N=m-OeU>UHXk}}Q2my>x zFvcAOGBL52xGBcpGs1%yQRuSpj>YeY2xDnuA1BFpiSG~%D2GWxU-fjoZvgqC(^b@gqyI1UhjWF+g!)81^9 zqXMSk$4D9pF@IZoy8uh+;d*~pt~@@JnnYS&(05mz6T+G(g@lwc_}?n1{RU8cj*oMd zl=SA$G3(UBLy$335q~RFadEi#EDIi+Lv;Ymtgh?A{}1_ui+cd~n$?j$?LS4tk6~yQ zK4FlrnVBicM7{dI@=#D)i8j}w)t43iE3)WJCMPF1m@Ii36C(gJ?bX5~$e+@lkg6)@ znW9P@Am#7xa^YIXoCvFqhteH~hnvUfq#_pIv4+GO4&0-0k+o6?c$NpsLA||1+P}A98QJKf)8QlB)vJ8*We|iD(7xI-5A+4|a z^IYfWkU(*_JW* zmacgtwhtI1lDM7?cHgt@`6MpS1ZLSsf93*P1!6rki*!>E#w6U|-e4QdV&3lwUSA%G z0bN#_{eY!yl}TjY02>MYlK2EG-I()!AfaW#7okW@k`_ z-PrtZHLu=j%NXKz&Y1G^0jwLy;k>IZnZRl|37@)xfQ)wP9gl zAjd9W9-WVm)4KOk)xIex>;{tFtYg5P_+I9fqhjSdtJ&dV{{Bo~^EVpEG$?-6gG3a7 z6mj$q##EY3#dJ+2ZuFI1)l`SaS(2O;8yWwYS+gB!;wfCZ3O~a+Kriy(e8{RTxOxKP%pq0(Kfu2Ozcc79Y;vt=$M$FH9 z!Imfok9@tchC4AafsAOqIc5K)prE4RMr!-{Hsr0^SXu-WQhzl8h3=%c#?ykq9*QygH5cRU<4K}0rxHy$aLaylK_w{^n{p)X8@}Fzx zZ4r?2PXWpFPo3#C4@ywz+iTP%5N ze}75i?Oweza-Q`4e^xG#%c}_Qe`hTo>jhz8sCIVh6cpT(a&+QBK>i&_mDMgY?7BWf zeuj%nOmg$1YtX1{?7vVJfHhla%m;I)!qv8UAvQy&)VHiG+hs%yW*6ivvCC z=ujgqBy=c^=@SrxVXHv3!DBwfd6_mCb8_`2iMIn}9x&=JU(N^{OT&zvog-4_`H_PK z_*@vxm9PUTPYvQqwva&Hm8_n`Aih+wTg#RIu9mgfeCM{U+zjrHzepPJr!;$Bl%Z#f z6ldn=(`d6kdf)O;Vp29ICCvcVH~EYr#TMm)nJMO8=;~6>H7Lifq~(vWCEfjY-&dw9etOG$0t?LcjfvFj+%O_!IcHjKk)BQ zPwgPJ9$cY3&#M?ePtDFAZ4OIA>~7AV0@@VVk`PBBuU*b<+k?)s<4K6Gjo&@c>^F%} zw&}A1Wgf6^P#r9&f#Ja*25KUxo=$g4c_K+}XkWg?H%yihV?v`zWo=u!IdJ9TF2^Jj z5VV}wqyalLFeuBWrVY zb&&8`oER(h8>FE;|Ru*O#)%GMSl~Ix1)Q&b@0l7vm{>Xwyv} zlRmgUqdGFrb^G+G(rl)(3XIHrz4J>ueXgo7|sulRTG?w*c}G~3cd zL`3|!yBXZwD~9~Kzr!IS>OG(0;iZa=vWpHTmqs_py`uI#>ZIDQvosuun04-V-kFeV zHl8zS>zJ7#bas9qA{#OeilyOA<#qJ`C=LuyNt``p5o_WoRkVrqddA1AwgCOKn>tQe z4FJ!=Mm_LHmg;$&HRsAllkiP#uJT z=xE~tAosv5K$?P~o!S<`4Y2rWqG7V(kO2t4u-Tj9P0&LC1e=b(%d_@62eyOnXr;C$$~rZE(~rfUbISj-Kj9;dNqmMOD4u}o$mnQTXlS}rYFgUC>S|VTalc_1F$oEaPJM^0JXS|Mla`yF z1Ae7@Wj%4$X>3H(>rd-ZH1m;M^FKwnRkB!0cwKhYfko!y$Bz;)uj5ro;5~>U=5<)< zi~t@7V3q-HB_2*rVnH8mdHI9mwca^m85tR1p}1r>E>mY>`An{{ndObjHrYQd(Z_u-NRqu&@9;6dN=*YYBSXHgj(0yBhSt zX+GEa-@dW(@+R_#SX=Lbm4>3{R_~oG{B?=?3J3pX4W~Pk zR&^h^Z@>D2Iyx%K%AFBJUHYtGKe$*}zyvgKF*i;bv~n>p%I|5x_}T8prFVr^6mf1AC!1YlGlApu3ZG*GV) z(eN0xs>8`cZf|c@3RQ8RJ&T4;1E(EuSH67l!j9)+R9>(@O)%{S)}VtI_M<<(rZ>mr zs5>TA@Hrqs%HQ*S546Cc1Ft=)fezi6~=&eoULfl8c%Z*0c#7 zy-Z9^vR%B4E4r{5ziQ-!g?6t^f+$UgKgtA&C@8A*x9yTJPSTmc*(l0u?pI_FvXagC z7y;!&f`Ew#>CShPqfeYtb7a5MEg-t~_xjSK$PaWOU^-g+K2%ZhO~q_>Ep#Sw0r>*$ zjH(Cy#9Ze`u{*+2_Z|x-CMLeYfZw+V6bgf(YBaF|Km@Ue(8%*;+4o2005ae@WBEJ2 zCFP4JNeRkobKnJyr$j_ICxe2?K(Tra41_QkOac}Twy?01(^F+dMPTA^a&(lIlDb|B zCaX=;tTY=2BdFDkTXYiw55l*WR0y+t{you%z>I@o<-?@(purH%)}T$#U1f3Kx4Y|# zwF{3Hv#7*y3Y0GH^`T}x77YJ;G72QIVPwEr6{Qcdnl+z(a#5#hTCMR3Fvj%`5{g|# zv{4Dn{XTU{xL~T#D>%MNxCMkoE+%$%m?oBMg3zqw4_31=9D=CmC&$1YcXYI*Wo~Wl zeSNw`GkOwKfyAj>|eX!u+|24g+|pN+-%Fw6oM-%PN&JEU zwT(}>Selt3ych6Z_d4mPl-#Ro?4sh}@CF<&Fc3d}Osc6)0E&Ub?eQDe0|9F@GvZe> zNRTNjjO6|t9DMwiKbV8Tj9^;mS?Nhdv4Br{Kz}Q%ldm6#wI4dm6cUiRcV9j35Ybnkpm#< zl628O7#-Hz+vQpYa3lW5-VOQnr{{=FP+nS^iF!@c#Kh5+Xmt%&Rc#XV7v$;!H+imj zl#N=+Z5hxufj}6XJq)bJk&OO%MuYy0}l`e(S(|;Y#gnma zIo%R6p`3!6m$N>jXRDF$vsHLOJb+&TdtA=*CX;qJ%Fk??hZu&-ff6_vsc%};(-TXu@1Dr-LGK!YA<3@d8kX)T>sd1ZMKdAe+cZfXC*dwDb%WG;pPIBm(_WBl@>Pkvn zfK=LVH9u9`J6?DJCKj(xqc`xvK|b@2@`BF*(E)?x@o!e=KTHMXb~hm%Osqq9pneL* z)Y}{qGTI)CQEo`~{DXs?vX1V*)Z4%!P)mnNdi&u3p|IqBZ1rmEmp*X2WEX7PZaOMG$>NtHJezr3TgqQwAjvOQL;%4uS?agmeQkS+f&z}0(aq{rEU9ze`HLBKPc$RNG zW+PoErKCzyU)b*fb6X?{SIUvZ-lM zNwBh~ru!Z3HY<#}V5Vq6gC_~}BEjd(f4IFcmascmsJJ=bi%BJ=*uqI4gtZ$8fG_zz z==2)a#l@@kr3ng^|7br?3gLZmT4%rVPBP(9)fh0D0AgM~0&1#l{HHTt_olIelu zY-)(jv27MEaCz$mB!h>*<&0w8EmQ9>!E8oLh;l}qM&U-o&k7vyk|O<*8dh*NMJBL$D881 z^F6IP+eN{rA`UBl36pj#rM~r^6U8U^GIj1Je!v@M#~~>aCSYx4WV08z&Da76IsS3ZdAYs7UBsdT=#Fwdk*fY+i&~F@pYxhm?mJD zmvOOJZQA_O>8Q&!vutY-AT?JMploi{gKHatqT%z&NSE-3dWC;YZ&LRgI@4yAmUL2DdWT8!vcD+8W;_ z1+Cv_go3fKxozQjz8eG>+(qw;(SsJ-*~+c%nJUK|St1oxRdYl3szf&gNPN5)Sk@5o zKg|5o(`|s5Cxq1-Z=J@&0*oO-_{?*lBjyHph5_-20s*R+i~ZHR<5>U$y}UXJh~#sY z6##cP7dDN7;f=?#K;Lu}qN;+aOYp}Qn9Y#Z(iYuZvD(`D8O5hx@SpA0f|j?{)fF&* z(?KgFDHrS#04Nia#E%~zL%w;O$xvnXbVZehMZ5ydMOXbi@W&@)jXrnxBiVdxY!7|z zuM9;6O-!h_rw<^%^SMQVI8$=cHZC4sn2%Zv=Px;UhK{*jMf9$B@DN~fvavk{#50`! z?d5L=8*sqHGv6IUFF>NZafOolRs&-lJtJeY-BLB6QVrU#NnbjtYH4KxTd49QleGt; zVl^F|%z~d;@#Etvf<8s&JCPLTG`4LhtfUHn9HyovLw@hLVFL;Tc*whfxsdSF=Nz5q z!?l*vqe-l>y)|DHWG1aw@ z-$o(q%+_=*|IoS;P*gdqsjempTQjv07@e8%tFNv0zJUooc$pioS{twGXle?XtSuW# zV+8;s?KcB&&{1f|7li;!3Ue(F;hN39EghYmirRePX7U&e;_vUopHWfGODFg&>r_}x zkD2u5DE9sqARmp3_udGm$)%`+uz#tFgjA1=R1g0i*!>Q2YkTJcfp!XJtB|vP3ZJVb zur8%vpj088TaN-49$_N?6_MKHniV%Pgjuh-!olRJQaUAe23UbtMZ|EORGJ3tal#_G zs~dvkk`7xVbY4yKBZ!5h5-0>wo7ssA)LVWlQ8U5<;Ov0KfMcIE4bLLY^qCM=G1KQh zV9iDu4w*YARse-H6!**}VD;k{Ks^cy#l*zmFro@;JLvL00^UWo}7f)Epmw`$dSxYZ5b59Ef8Xe?IJDpVdy2t*Rr$P(CFP(=|c)u;9(y_cMR z>eZyCI&X$PKTc3L1I^f0_l+-_Af@_~0xLHMDs#|SLm&&Bt8l_0rueqk@xfU%71!EF zSk?w>A<}rwC`rU}OUu}{%Y#h5NB-ocF?sVCP3k#u#H-#A@NYpUBE+eS+I_8Ekq{EgYLEh9zob zl+-7A;Cf($0Xz`_Nbp2Nv@|r2UVC`d zXJ==FI~y9>0|-dBrwIuOfR7w$Z3TS=^n!w9kOk1q3^Hoz@9ki3?{2-H$f5!GmOMN> z0HFnW-xEs<-#U~o0{2=)K%b3Bpo>sQZCtJoBA%#w%uz(@9*kiIdGFQ}1pr1nqu- z;R7csB_#!wEGR4#^f(QH_P}5`YHDf=9iaOTY_yWcfGz}_80cSfcW2UVtW8f(FDtV@ zTK*ABBb!m3oSX~{n>l%T-D)@xJ4eUaMt2TLm{yI=nhs0tH=FtT`udlcm{Emlr7)<5 zy1JRQwJa>Frp8rYUmviD-rn945)y^YFEy3%XC?gNbcb!~_v(hT?iU9-nkJXKTLRt7 zr*}d37Qff!A!xz^eRP>Vdw_u?;&a)JrVv3wC+yJT85S;Nr|qe=1iH9IK=Jd+&Wh-Me}~oz*E-k_-ZAWLeV1GN{`d$_Oh~))7$$l zTRK6}5`#>j(R!8}FAVqTQx2QC7YiL$+{y1WM>oiaD&JK+B%*3)Xk0mJt=hd9^ZTWy zsi!x113DN}r1X@OL}1Y1b~+}ewHy7yDCyop#Y*b@Caf!$H?VXo;?W759bM_e*M)g` zAp9pmd*Y964&~9F(NRZGLj$SAYmckh z@R0chAD``Xc_6gMdbZkr64d(#5@uWQ%^Mr<( zjt=NPlcS>*<>mB${6Q{R4h%P=qe}9>H`|dcN!x`+ik4?lq=G(kExx{>Yn9CB@*(DY0EV_2BAwKZsCwYIVXpOb+Zzj+fF94rn-0d!)nEH7)G?ez$UW!}xo za<*JEq@hb5?QzX}p#Iq29L{VHLciHAETv~)0FDX3Mb9|yO!-3nC8qb}g_h%EV`D#m zR##K|%@v~)zxb9?Be(N*UU=^oS)BeJWArxa+lycJ*rE6=x^C|7;D(&;&+`FW5mm)83pB);{+6Q*SG1Pz9~OMtlBWZ+tHJb zIS6UIgP7?G!fM(n5Q@mE9ytr3x+X;bvKAln76D6bnc-WbO3Y?s3qiPM)|v>sS)up2 zUjy#aK90q4HB~b#%t+jL1*_&u_l27B^PjUHup${dc3pgy1MFN;x!h-CYG*AC`{PqL zr`O{OMTYREbS<$LP9$p^0+QVVBnjZ-4H9{op`LJ)Q?bxtE=@{IXuc5PJ#QJJ08&x4-Dxe zb0Y~ef~X{_*j%o=3kg%o9GO%>!@wg6U5Q~# z|J#euJQy>Ig{_%jEKo*%N{$JS#*kfv_tI)(rb z$77VqOC0xDSpg$a&+l%hzMChm1d@2(0|<57h`)ab0RCLQ7d~A4K_pEuG*41E06Hwj z_9SFE2si*WV+&hi2%_#k#8KtHFXo;GuR;7{WS$zJw8x76CfeynF;t+MciynVYw}Ji zj})&w>djFLX<6B-Z{PIG)Ik;i>uN0Y)BjBlJJB7Im?d*(u(zH=yY@mor9-`|I&ZL0 zxgMlm*YX9O#sc2gK&p>{Uaj`TroQqd<#kvEvJL2E?eFU=DlN4&Gkf84Tbr7?u-2PU z$NTy7XJuvOLz8a9GEqebb6PYN|7vABGZo$p6@$%E6}YH39 zPoYBF_o{(0FHj#?1^jCQG&MEBlq;PA`2wgfjh3Jrw{DyaVrFKxIa_1v<0EkO5IbZC z7JS$iL$+XDa~j?b6{k21AIPf$?e2PqDg0L44J9R!d}@E-FPejVRBJw(6CE85a)+H- zxjJJJ*$4iMcSfwj)?*d#20=P<&?Dt{v-=)^3Kkq_?%t4f*&d?<4f_jRAY_3I1G*GE zwaRgmPUmVc>q^Kd#_)RMnNH5m`n$U~#|tNc061B!g^!0f0faMJo0l3*gt0^lq>j`WuhC6(|-=ZK1L}kM`mS2?*#<`P#Ho_OhrkV zw|01F7aAX!vzqt2cagU)L|pygwFG@g%|qUNiMW@&h;RY|{Bz+wvE94r)pWY$g@8*e zB@D@W6qFw9Iy3N#4q1-?u*A~&b(zT$5+0s~cld}&csb~QY+!%=1Wu(TKV=Sax%!t; z45H)tn3k7!a(uSRH2t61L`L4cb`a&>5vSWoGu36tj*q=VE*+b7TqOf0xv57?ZQ(sN*Pydx$haG^gC%J#l|4*7$= zl$0nI8S;UzRNQ#Mn!Scbn$lSU29k08ucYJSW6;x12!6XkLt|}YL&+8!AtpLn-^2tg z7H-216eFV*@S_DlUnGK@@6XR>ierk1h=3PC|2pI|zdJi*YI+)#MowP720#gRc6K(l z=_*Tn2*1Z^UO~af8UjDd89#F3=i%6(Ok8TN{Py|khrEV*9~7mFX3@bO)r36-P?C_= z9&=Qo7x8f_!1LnE4<5y1;%uGJCf4@7I-2er*JxaGwbWk-rjaO>oA?mj2GK9dOkK4^hgf0stVhePPZ&#PNdRRWl*`EIgr8tA z%37KiFQ&%F!H+pOfsTDpf|Fb*p`)Yo*vx@n4!XZS+W|tO%fZ6l#s)GZncG^k!DSEp z^a6`sbEDUl1LUhIdV|MVf&TLUY3#b=seJtXAtd_{vdJi9L}uh<%chdO$tEk&F*+zR zQpnB>8A&#o36b$7BQscN#gJkkwREFr;Mkhu{rNgqn56jm%eS{ly;r0-RW^WrBY5Hl zNHC%jUDwvvcM1SMcrXtL!2XSanOht@l;KKT)r-VNoh0=-#rbv!M8)ygwcF?2onzBJ zZQfD|yo!!C`8A+}7@{*1&M_Ud@@GJ_)!u!yv7?H3n&>!dUKme%^}&mz{r%N?37W;V z9_c9vVHD+H2SKp9+m4PNNRs{!@)#VU&LKtR|%E zSAl*h%zK6^c3J7^yP#MdcGOT;FRbv!11`|M|7T*NBo9cf{=2u;8T8VK%aG z`j<#1KkUd+b#;i$@t!*XB?yWOuPKlvXA!4)FMc*!Jp=;9ED!bQ88n$i>`U_V-Hk@s z5V?7I+ZzXg?H-9z5`Fz1BXf(^xpZ(5h)8e|mF_9;czD$=WcmNRyT5R~*yz9$`=&^( zaITIxu9;2ZHhZ|OXTvw#s7lq2Fe3A5U$k4M-=EJFve z3{+l;-BK9%5e-wW5HFw{19Lc(U4-C&9pX2fGz8bDnnNp`ezaPYSXfv->y zb~-jTIWaLhC=}|Y!$^k1VC3en2y)Uf6TMx{%I8rs=b1nGHUt!xmijXaPA&m!#~{jh zy#D139UU#Ow)B$yzzYW5qWgEb%u zWEObfux>SA3xb3W*MtoCT@#@6^}oJvlh z^KWf=KtKq98t8*971xN&Ov?ukgb>Nsy`8@$OZZ8UFo-(B9f5G&n%6cpHARTK|5CHE znt6TI5*BAXDRR+CSMi`{@B~9z$-?wBh#aeHYo&N*;l72@6FW6^)oXd^y7$Vps+@?X zX4`sy(>gm{u5BT(4I`FEs~kHo`~c~@y}I*?2trx;H7)WgEWu>AJ8Em2K*W*4}GbeffS#UH`eR&0{eR1+iS4lZdX zDtk`gGY#$S)j<4RP;h@SJD-Q@RO?pm(hbRJYr8Ci6~%Uz?7pkxqW~M`=jE+A#edeZ zCA^&uAyTzT_Z_X=O?6z-zf-OXgs~FZ`H#2FF$?lUnsIof!hQ-!OII;c>_vRws!tin zFaCHF?d9Y$V`DY7weuj{YkAnRsR<3T#>Q@!WiYybi9DjAqFP%q_Eb}Y)Hg5@YFN!M zBUsysf$5c%70u2mI&kszW*+D-%f<1#M1phsxsf#`=0`PiPyW2+;a1?y=)*NAxcG*aFQf82UZ7#NPLTLWld|tV679~p> zmDe2YDjP?48vjK>fp4tVXZYnMV*-Nlu`y|DYwn$$yBZql zgGB?&qjc47)0Dj;M6?_bOe9eAxJHHbG7+(=+O3Xrq}x{od)_L#9<5Y_9}bX(DIT$N z%2qIFgq;jSaMer|sewUajd$_X0~t;XK%LT$50)nzh2Lt#zE0E_tsd~7ZspWW^?-AW z@f9}o_O`anxBxk*NO+EDBR|g$fbVuIp)(2|_o6sr@}^yG*2No1XMc zOf)y3CccZB^<}MZZzp6>_sGznSFCD&(uS}b_pvq5AMOTWeS)Y{a3OXnAwf#P<;6zh zL#GQAih^KUil)$RO&+iY*NZq58CEfemG8#RT&?+S!4Kw>y?s1}SUP zom%dH65_LYQCCNF<#B^QEZTEv2PsQQrPgIap1v5e_o7)yJMa;+$L~`x83(2pxPjO)m(q_SXz82iQ?zN z{$0qMkcBL$u&OF^d;68SG?t~66}pf3`Q#~S{#er!gQC2=^z_E(ktTPwv?TpMS!(x2 zeE5(yHhQdQXjocRwcVF(PxdACpucAITe6gog{G#!w)LGmWBqwA^JepOFX`PGXg4z* zpoP=Qxwx>r-2XjTR#K8c@pGw0f<3dR(@{shCFUk%PyS_QZZ2<<6?4DTrT)m~&K+ZI z?d;Oc=kW6D4_Bm;q`&vL;@-IlDU=`4N=k2w_`?mDg%x+BWF$>Jzo78o@K4??b;1KD z+ZIeL_WNl199wYktl#l1@~8@Xht(H2E`$4fZa+rKO!p3bDP^Uk7%eTMwsv;5cXph9 zcwxSJ_Vx9BEImwn^-A~KH(?_q8!iq%G5=%3zC?&t8yi2y7&tp&1+Q5}tvKgP%pfm^ z7JnLn+zbWb(p@n}>$;nSXOel@~ILV6KHPq)Tt*9U) zHdHQi-(5LF9R3+-rKCh=ZS}6S6PhQDAbw30wrxE+{E;;hs-kt|_Uyr}2mOw^6U>n+_v7mB2q zqYUE4rfjl9j*$(5SM+~+E$E1djMn&7JzBTWgYt2!@n{GU68O0`^hp;qIdjlNx;8+%L3p1?tXoH}jh(aEOz>&j1kd*D z{n*LnOIlZEUI-y&%yp!0DjHoR;YKKE2U7*z`StPFO>cSYo%5)RjHYK!O93~PMvwta4HZZsMViryw~h$YJnhR8F> zO>=PIEkPzbo+ApC(!qxbpwy`_H}?fh{IF+ua4 zkyI~lZ=ft_si|Qce!Mpon)Y~yE~$$qvL6@@fbHGg-6tMSlS()XG_0{kh16tRlqgU> zAW;6s&cc$`*{KFvW?5xb)$pn+A3eQz!6-nUe!(0@O+Aan@~!d_Tp(jK)#d85s3E%Kf#1f08B1j zx>UdaE2-%i`J}ODaF8}7fiiMherk{Yax%|vqKi?A_#1@h$)-iDgLLU`{<&Ik)uy7A z5GdL__XdAOzYj1xhGk<#J1Xe(pPXmRezRLAss z%Lv`?b1x7ZQ0?I_HwgdL8iH;T;sq@Y7Kg_(g4+w|gES%QFv!4z_nTV*Iy_i5azNNo zeZ^#RI!fkSTN^6|w9N@gNvG=@3}#p?Hp2lafD;=kg`1ZOR?7JvwzFqLL8%=U4>^0S zEiI>{&nG{hzjD-@G_}1$sElcftIQgMgl)f3$ld}?9UtE%&D7DMA#s?UVOWOrLG3i^ z(*sUBur_8YMeTr15%loTkpQqtdj0xoJk*O*8VUFh0O4zD4EKiN!3AW!cNTs}i%2kP zfW{jl_MTB)q7g7rBXsljF3!*IXm4NN-4zoQyjPE|d-yCooW#Wi;Sq3*RE~ypM%X$Z zAMSu&8~{Y+D=+{6NG2#O9RBnv#nCbh`3HRAgN(4&0})YdRP|tErd=VNMmrcFWfs4+ zMo?D+1RyIb%Mlk~FUK5+XKrqODhluH^ngSV*GUxILl0HV&Dqkrch}b5Fz<>s(5X>6 z@ZWpSDk}QD|Mcrsz84?}AoYb&7%=)fdU~o?a0Wu8aN8Z7oXD9ZJzzy?ZB^ut=QU}X z+k}rDyS)4lLZP|2Ia`FYB&VWc7jT5!MtZiHrQ-U?XV3IYEEH5!+QBRU7wWU>OzHF+ zRKBY!%ClN#=KZtOv^wjraW6!G#rSKLTjeKb^T6Pst%Ze-zP?Jtu~i9?mgGE(N)Fec zJE@!4XZI=Uml+Uc`2KxQYikG*yM6)gDqE_S6z$D#M&)2WZ& zd=&VrMcc*svv?f$?ds-cxAW#VV6ISLNH>X%_2wx0 zI`U{(izrmtV{m4I4nAXRLqnkEl9C6BubmEU1XxZwI`yG)J}p)XNb2R}oW)?4uUr}B zEBPfhF_QpeV}(n~{6?Dp4R!Sh4h$YX_D$kN3?vk;sn&JmC2r3?EszWz0`e0k4ShY!b|@k5`ov)$0>AQWq*Yx>WZnv~Pi z^%0SAg6W$h4mlVK8dZmm^X&QZ9#v^9{-a6p-en)R<~HYe3E7yKQf^n3lr+Ziu~d|M zWo1*Jr!l$2@tQ|~zi4rOerwAQjX#{Ns?*KD*ik{4heMRLb;QmVcm>Hl$5MLF;QM!7 zN5?C;xRsdb<>d@o2021DFr=^#0i0-j<3RT8 z@2fX9eqf?u;7OApZQrfDgsW}ICbreelQns^`%eYd@ZMLIpcfuQq zmBh#Q8R)-;cXriEC;Nr4|VgKsP*!=abdcBzMR1kWY)W-@g9*vd8Q1 z>;I2d{O(N`p2SUNm7EQ8dF?%CQ8BUZM3L}DGoULl7>sgFNZglcyZiS!I5^sTHc{WA zmEI^rA}SdfnTCdjv$HcJ*(GuDd$k(S=I`G9KJZS@?Q?xc^Kay39iTe^L=kly>VZJg zsi_ihs-dZ&=?)g?14}7v-h>zI$kz69u{-AZMa~q>$J>V`b&_^ecYvCMIxHzEiHVMj zqXFKvsEA)QNxh3v+IOcDM3WHt_2Y-(xFJbc0NL8w<>_^_f=d$$ zFDxjKpNM$+bZuux@j@OsOo)AeW6R3PT~beMu@VB)4tja$YOnV$_Q%`eVipz_fh$O6 zw==OADO`mQ@87q5{ko)gnS@D=E0>`Y6h{A;y#Cp8VIb4%g)?aWSFKHQBxFvll%79u=dN))=2m@%SywcY1o07r(0! zD13lI1;zC+h*uyGXFi?&kAUyKCMIb+hBksmg@y6Y%DE3_d-M$jsF<5r+}PiE2>Rm* z{GZjMEg_qtB7uCC@5SX(Ez@z>_a4l8^;+9?#l;Uw zN(3*?cUqVH8X3l?rPb5!GBDtG^Cll0m|Y%PXJX4L-CX$jS&GJ?sK)o9?VRZm{lenG z*9joh#mOH(?tb%z38Kn)1K=ZSRIP9kgJP}rtERjhXJy69&Mxg%L0mkoCSl3PYZ{s- zZ|6VUXYUW^(XU5OlsSlifqh{?o0__eL2Tu12MqiI_tqj&tWP7P!7>_tJuby~t5?Z= z!pHWJ?q8W`-iWA^oxSyNef`=@B{5s8)g5eyafq}Ixu_5e{cvM_xS+6*j=Jpj$D9y?mlRD_ zLh^&M#$LQv&iDOYUd_EpZlBvAG_n6vPZRq4NAJAzqLKykDIuHXmqk%g(Mz1OqjiU7 zR&d!SxG-udlKacULK_#oTe?ylGzp(Q9qOI~eZ$%=di4QFVyiZnA{gpNtM9;S>9;js z^}>myhpyFOO~>8cL92&M_GuBn!&H;RxpVIb*|fD)_kTAJ4=d#4PpLwYxX*E)5B^z#{>;{=o6qS${r8wWePmA>+8%f}TFIVtjs>UCtd;-(n1A6a|HBEy z)yO^uN$HqoL{23*uk?xW%>Fy4tTaZS&yBiTfTDn2HOS-?ucb~n_ zyZ3j__y5nOUR=YtW@fGDx$j@yD@aya4E-VTLkNP<;o>6l5QN|d{+6R6f@d(Qq?RG* z83Y#*RCG?>opX67xlIBctV_D(*&CY0(yL*=rFbr<{(|1{rHWfOarba@a0d2N2~2~c z;+-2U%#h|`{Y2bqgW({)%3BsYPp>CVo_5ALiiup6ybBQ#g9&HJE`x3Z$*C@%S@y}r zqwo7;X5jhlAwKiR6s7QwBy1B0PmqBhF>e^L$^AD1qEN|21VTkn$uD(bLa0L+lvqN6 zp1G#~v;k*o;kKaXu%bouccAD=$)(W%X{=gnak4V~IE|14C|jLyqzE-{_URK8r2 zqtgYi!B#hp;x`ihk;iS~$7C=#KEVP`d`89+1l`vqAx+6C=JOmcI)NBWHNbz}h- zc`ozGC-?jw7#SgOSn__Em%ywkR56Z^cUDMJQ~QvWW8|>QttK=0txZQrZwUc%KHWJv zSWItd*rTGFS1FlAfNqO=dT0v@_-#H13m}mPeT>>DOiX0paZDy=d}cjU6LXR+i;TwD zVryt8KP(6R_@Vj``tSkHVpJ3shVFjNsoxcD?RBS=9Jals`uw>DXHv0-)B6kCwBNsP z2tt$tV{p_ayhLzB$4%_9{nVcotjw_78;rrcj!q0t0|MdPeimH9Ub1; ziA=9~$%1Epu24rqOM774Zd6%VSP5s2=;v3Fv+<6R{y-kiq#u`+n>I8wlqleZ7~8H_ zP83?2CggI_dsz|v`Lmrcf zJlvO{*E-7Izn|qug*e!0Fq@8kN#dw|Bq=CvtGm5(r`6=#XfhJ@h~V}|ztoD%V%0z$ zi6Qi}G)-Ho|8oqrKDCV2?eXUJK+4SS6ppSzR(~_PF&Dq*erH%fbrtIqQab6>CSG&Z zRxZ0)KEBMm%kBD{oaxiu#kI9LGCtSNn6WszE}7I<$vhJt8?p+fmN>VcE+dFFjKvXk zU`yW(DppokiKVg1_i3<2DPUM-4xJ%)49WA1urjEM;6ZxahyQUO5(Z%k9e?}(wPgu| zS|l7C`is=IXKKkPsf~@zcO1^G5ukVVwq1i(W6f@Ohzr99>iz~eq8aeSj>ipZT0kq_ZGZN&5LU7 z*4q*8g>Ldc^WX4s$k-ol^{QPTT-exjadsAohH*KSxV?M#CHcv>ZWA5#>F9g~m6i3+ zRMdkrwfhs`b=RjeSbNoKYAQL>noPRBny#)LyE75O!d{tI?=dsb$AemW>+OQTItU52 zG&aH~<&1Q?v_JGkicx)4lcISio#H7DPe_Y($SylzKY3R9)z3{yxn;d5`>Ah@xf*RO z*ajI#;-FNDW~_yTNqL=YX$lFIWoCY#t>4?)qA%4}SzVojyaoMS!I3#!e%q3ftq*0Q zw7Q)gE`1jueS&N~j`&(`h?KAS@zj*|dY_7dLiV95W(EN`UT=T1ee$<$n8aCR-wx-9nMe3_<7W)LL`yG&PlpL93~MK>FvnrPvt)xd@4SO~9vb z#%|AoydD16<{(qEM@@mM(GcQ|iT%2UBFa2`X+-nBe+#;X@xS+v|6I2QX1HVlRan?E z6?j1ho)rEaI5h+R@0Hzc4f1oCA2)%dsvGs~lemYbhK zNS_G(tG--`^w3X*iHz!R78ePn^5#EbHxKvudY`4zn}p-3wXz!MD{iN*=LfQynoc-4 zR$Ei2pML&a;C6b-VYkM;wHi*Eo18+IZ%9DUo5E8m;=yQ7nT<3@E$`TE3G4Y^`onCY zkkB|c4@OZQCngCAr`sv=fE!pipFWwx-*Ufg=5^f32n(aBHn*5tpDYi_;3vEkpCV&) zcr|P<5-n8Yxl8_YbL6>Lgl8D3t!?OC3GIBf856|GgYks9!#~E-ctS=_&gZ+H0)vDE z88MNGLa(Ukbapnz8#&8AO_oBZa%yaBik9{*4Bp~=80Ur;^4*BBr8DRE@6S|JA4e-d zH#svR6HqS9dm-0bte)?FE=zYUPYF{psCDSn{kJ;&9d2AXRa4`kATJHQw%l_dORKxI z{W(0GS8y4i?s>MSe|1VMM9(ZVj7*6Id)_}+yI)iSHuvnj!?uxpkjEAA$rhW%(V9!Z zpx+f%Zw4Gb-Q9R?6hVa(CY0b*<34}>p{y(j^nP-dXQC^IPY8}z>7y3|;v zYLnc;|Aa$?L9#KrBWTr}|A~{pFGh5zv9OW<>lc?8rr}%rF1~i(aCFSx*S&vVS7ow% zyd0q*okELUM`Y}{i<~pHMxIppp3=lOD(?duBSKAsYvI|xOpBM-?{5KpyEEVXe8*8| zLs=Rf?~Rz-eN(H7!i}60|CE?GR=sV&$ABxEs$T zjE(IJXWs7!QkCdUMPL85W~8JVFB0vNojudn-)N_7!{gz~W_*Q=ztwBH+T%SvJpxNG z2CMq=q*&1v-W``NELD$z15w0gMB>?&nui+7CU@-c@S3Cq zxVSz>L>&2{$Z*kUw~UUd3|v;2j=fD#$DDdKJ;^HEPT3f>(U%aN_ri)g*T>LM#(V!O zv$Qlm6O)M|6dW#XXH&kCHqy}YWYXNx!%pAs`qs@ejsGLmro)&j;AOWr8+O@q(O+bZ zZvjIB8www9)xz>=UUv54#(Q1UX*Sm5+KvvQ9-GW*3$-4 zQ->ZrxMF1+tFgi;Wk4}S-1!GuX>oNBT_4yz-3j{gyQttYD}50WGH1`vo#3aW#D)+N zMH3Q`&M)dmRR*%M>aW#oGL43Eh(jw_LfE63nH3kPP<>lxR3Gk4$KGrV9sT%W0$~!> zkNgmy85v2fjC*EINlBT%nl16!b1>W>%YlkAfW{Jhaw;|vaDZD6iuCBY@r2Q~Kg+XC z9ENUS0lu0?nZw7w$i=b6B;4O0bOrIczet1>m7rlNt8HAtCA!2Z{mnpC?j0 z{b>UNP7b1?mJv@CPEM!^iKVAo_!NFM*1`8+;|meb%)`Q(Man1zT9vS{W)f>hIIQ@c z-MSn%7?q_5q$PSSey^;`q$>VinL2oSf#pxPo>ozD6Nr_LLB?<99%bMoDMkV173#-+ zGEZRAw=g&VwcJI-wtd z>~<#KEGAPwsI-{r_CqeA(1SP=p_L5FxO$IFOo}Y7bj^_urK|w6R!O>tFovh}R%m=IMFxzohB%0|CWipLy zPN*;zOu`a!bH=xpyA(!kmB!%cVdK|blRHEw8Y{nl@$W)En;Yq?zmzWMf@Agc1Rov> z!V)+vlcgFRw<6dQ9@5o z4;5Nny~<}U)!<;^p8m`;`0-3tGi7&oceJ7UZ4!HdrzibrUu*I`k8@({hghPYHD(D_}AeIW6xVxpZW)zHV=-YPsBy&Uc7v1yB6$ux`Sy<=XIGU=LbF? z3(N9TzUf|@%}0G!nDPtgLclU)~%Y zS+Pfwph)YXLmxk;81)n9=1!y+Y~et4b*eR%#kOE)B`514Azd)(e|*C0{1mFO)cm!= z%J}>_8ua)nuS^)1sMu9Qz1r$}X&TSO&1`)&cfsBZW5QCRFc0(5RM zQAKr80rX}a>VND zu0w;FgQrSLu?ysay2(+ad9@xo6#Eceo2If;PZ!CD}nh2 zHZ-U+j70sN`{06CET||eEBer{J2<~zk8nN@kT)uIX9{5V!=jT6j#H zfQLE}?g}j<$0p@Sl=wr*c%Ai~Cj)xr2!Ko^g%EIT8HpbnSXtcy;+w`lR@b18@LF}U z14uwKH7Nj=>wzvCM|YIKVI^d-`+1_YaCl@8s7hQs8l7DzYin9*{CrF=-b;!9AXHLX zrIw+6`EtR{g$sxv4=`kM8q&6UrF58-OH=8oyEuW;0;pV#g@#NbtNU1i4)kk<2#cxO zhi`B0YCDC;3*|nHkq}K=`!j&2iZu+{Et#SMN{XL6FT1Ju`8Q^{=A*498O<(!Yg$@L zw7Bb@ZW%^KVwxLIXzN%Gi+$Yt5<70}W?-l(r(*RcdL;m3?0Vz}`_IYxxs=jeYb>MWu#?L7+V0xU6|LEBNl=6Tac-SsY0dt+y5A&B!r1BIK@{K}$KpMXE zD2Vlg^FKTG4GQSk>ObsZreAE6;^Oqpcd|?NGK%^5>FJwW1O_@)BDQCSM_6*;Rs>r` zW2i%Kawvz^2e}YOB1jwKBO`4<(<>;5xwzPN+U?3 z#>i+9=otQrwEiNJIh0Ys!T0W7jY61Ytxmmh7jq4ph_5F~QevXlqO-h-if9H?o1gOY zDymG(b#z?y#PTRo4Bj8Zy?v5*Q9wuMcd=gRd9>yiAZBoNs&DTw2LR*S)n_|uh2sK?;Fk)V3>rxzQW%(f96GZ$F zO^vJ7t+5q}&$p6rE*l`pHE>fI?{1EKZ*-(XhK$#JMtKEK@)=MdPLGC_jiFNA4~cc# z9YMW#cvpCMc#sa*<|ORbk0C(m!a}R^`@tpxM9*_!C?t>-ot=_A>O{QC%gaDb?fdr- zug>qg#l<(aOCqnJJ=E#iF5%GgX_Y)F}8N{W|W& z>T#c2v)1%;-7n<3noEI*huVh#0W|C2P!k-C4UYzD0TNWwjda&~eW=f7Jl>*QtO&NR zWmL>Fb7z65NO6V0&nM$Ve=bs)&Q};fP_>qP$lhWRsm1T=yLx+lypYXI^?|qn_BZql zkhKVzC$TTAeYY-c0X4loQUao0XyD}Ossw3FcX06A>>6CKasZ5c58HO2xK+zT_?4Sx z4n*4e2KtgB_tKJi)6URxg`7|z=dul=jRPD4=>SssceI>qi^(a-cK2iw{POaYZEMVI zx;v(v_ejZVnsow{s+v=0uwy~P%&?7|RV&0YjYso9fBSXCr_s?+!1b7y*_Id*5#3{D z9zH#;vO1>=OBZ4GM-?N@IUg*YBV*g6gd8W@XXO4>LG2?6e0|v zBG{sYV8Mv+{Dp{3%7`930IJ|@4w2JIRIKWlt%F!Y6Pia73pnKH$J z|I_OqA`d8|e%h2+HC231Xd}k-G@$T{60hsHHp3@*_6d2wug=3@_#uM`dBOfAep;*7 zAoB%0&TV-WCzBcwnMam|gK*-I!7r7A-eC%8E1NQem?f(D@C3{6Not$V1>qp^k(H z67ohO>5s;P45C;au*o;Q|4j&$U;VwRAsjHEtXyL?Hw~J=BB+uax6Su;&CGYk;zdT6 z%DU9o#BHdD(!6dBjX6}bv@8dnoQ)SNd0y?7RiE@#RLmtNn)VN{Jbx~!*{t*kANzs7 z0|ksvxpW{e{~|gX8=r&(6KgRx^;TkkUej{U^_{%DkqJ>zk$GvBft3+{WSX|R!qIb1 z=dhf$_jq_HDihyJwVE?+^NNe@?9mkDYyu+|B(m=#6^Dn;J^&7dnGU0FN1!YoUVu{J zDZ*=@$etgz($Y-H#A6y7uoCU-Uq2-biaf4!+~tQtiP-{OxqznJ)Z_pZsh>*y{*jO70iuT8%?1P@YGCzu0Gya4};gmRR0ME-VQ>RzM*vEYTWc&a> zWF4e`LG_@{cULWm|Hh3abSLz?i`sndXHM`$fI~MTijX5QfHj{+U7%JiGKE^E@Nqzn#Ya%dPA}jqJdgx$-!O zC_EJ5R!E%GVp_UaI=6=PEr4N3GFpmai;Lv@K`<7X^Dafghnqj=bdh%)^}!=!tz95) z3~OK?f`Zoct2)Hs-z9071+dyKyL8aD=iH`Oduuo7u`bzNDWWp<f`(k4Bk9SO7&mm70Ct1oy zZr9nc&jwvyl77V4FD)t}BqIYlVj90;7>RLaUf!=orXsV6fn-vI*JpbTqrWeXwxPC> zk(LJTnVFe<2eVjUY1~}DD=4g#kf@!Sj0eb@7I0bh)4oCNy9+yM*+?@W4t$Em6N`9y z{d;v0`Jukf&!KLUFTTFl8$*5z%{;n)w1HZn4FGLJ@l$Pyr5kWyfXV;}8BiZ&lob#2 zI(~Qs{kDs;PmV}h7iM5sC@mUhXnA zUbp6`eLgnEhJ?hCZ&i12u!2KPPfhjlU1MB(n$Qe_1i$Mao2nfciJqSMA4Sa`g*?57 za;jQqEup(;0kjm7Bz|5J1p-XJuLoy$UVaNd%SMXSmf0?~EPqYLN{`3i?<$mzKg zf@*3}ZxvOnzH`mY3iucRt$@8dCa<7iw7GFFijrwm0uS%Gre-ndS6o~wfJ|p)C2x%6 zZl7$^@X};wPjXF4R?SUqY`iabIbw2gsrU9C%E=Lxq^%wuel7xp z);S>&&~0F&C0}^1+3ZRHxt_6u^RUdpz)XvCo3``t721dCcm>$45-xkvO@Gl(CwX~! zM)q->I@8&@bYQn%->6(%MZA^fM~3$HYJgL!+3G3q%Dgf&^FyiC^9W}z*27+krv+Uo?0Evhdg^RTpp1|X4EVEHaI{rHH+v{=tdGTG=O*wx-X1Dsz7o}Hgq?D#GpCdkN?9~;*sHhPN%3PaLB_*Z#7Cv|2 z{_b_0Y#ZBL#zSAUTge_hWIr$f*^@-EES7An`{Y?_f_nNRz}sJ3Ue3zTH!|HS%+9_& zxB1BE*a5m-qtisT=)G2f!XH1xO^Ljm#+}<6OIlDo6QwAg-{2}MHv??U<7_RyKR+TV z)o5fOGOS7AFN-7IZFN}uPiO-$P;|7o2qlv_!84dmRu=FKqTm04j!-W2**+s&#QFO( zUCeLn%vxJgzJsTjl>gnj|CUht8#bAj@y|kd%=4DJDGe2VkEx00KEnys*Q=loRINsF zBVB<%fu_H`{BPN(GaPag?{DGJkN2sCup_Q6SONKBjl0j5Bu+42FS|eft=kuH0 z4f=_Pu)N%Z!BkePbDCwbSHzS0b1!)DC}@#Ndc2WdT51IxOOGo@D4tOl5rnJMhh14wgYbhArGL4A)g~zjkujd~>a;-xVox+sj@yqITseam^i*Hr`yEzSYXAbM zr`K-B_JWPlXMOvh7nky5Yw%#1elroZc` ze{*Gp)pq3vm)%-2pPS=i`v;X$Em8qb_s{g2diweZI$wQ}fD&_cw!hvKqP{Uzxjt2C z%;T`7sH|*cX?eT&4aF4C>78(xAf|sj?*vdq4hIYJ57e!gl7NY1zY!T8J}_Hv7ZV+w z_o?;vQbPOBqctPL_w7nKIJBT2G7%wRT|z~2JiKziyOh)F)<0=j)!)CYFS#@>Ipmi>^3fKa33(z%v zJ;#t!?<~xDnNdA)ccHjCX4V~6v+=qx5)9CZ{bQW>wU7iS5xqZ(f);6PGd!50k(8hY zP}LkTzYyBGBF2E#jPTmNsx(o(&~k2kV$>a|y(8fHfq}(Q4Ujhp{%xK0C%AP3yfEPE zUgutz>-;<%&38uj`#G}xrRe%@0fvHH0!l0kZI2tsR4JnARkq$rM^=Uv6vzYgDi+=$ z%K)TY5o_FGV2si|a0`J2{-xn-)mk>p>Y_q#-|iuJM@2b0o+?4l;Dk)=Xi$4QI~kX) z?vKcX)*HtTpnwB|x>UQ>3&@yuN8WRdcJI0FoA?5w^p=uBZwrW$f`Vp3LY;X9v>+8p zgw1?W?Y@+ksBBM9ap1f=Z`Qu-C5T3&Qbu9I7xo1@uB|(aantrF#Gn3` zjx1g$@>2sg5R2*BP3=D(a-6p?Ry1}-8d4{~Yx=Kz?4vOgenVMq8UK)G7 z-$OdD&5?J?<2)@bL2RQJq~<{NA)tS!T5tROY#Mx6SyDJ30N6&#Ad2v({9iV^omqYl z#sjG_-O4F&td03*p0nwTqxFcVe4vsK+2di51FszS+=#(eU0vPUx-2dZ-|s6U6B8&} z7-t~+$DWs;|NZ-S8+p!56VxCyQHssn($chqgzlIh4>+VutrsVWu(AJnnU-{)Bf$~O zQ_`t0kMaV*R|TofXzW=zX5iL$Mt?B^Z`JW_p@`y-pumP8$_ImSzL;TMI{G5_*tZO? zpM}6hK8tX^=$Wf?XObuy<&aam81>PY_P^RBPyz3{9=xs^w4ycxz3>=Mh7Z**>cyW{ z|0T7B9DFjrDAe?i4S7t2LCEt{fd-^_?};CCO#0B7>#0yvz$@zN;su_wv4Klo;_B*( zjEo!<6f{cf`%Jl1%K~(Q=Dya|L4mvE)YQiy9-*P4a<(~=YiMW)0MV@5qKqK3?}~yp z1@=>>U=cPpHXg?vLPnjo8jG28h4EsITK9|J;PHrn-ajC~ZeB@6MMYlz&mpr53%9#t zs1bhk#J&V?IXyiMuB#s?N-V|$-vTi5&wl4V^A{0)rEU!B_;eKro>Gc+h7#F9(s3jPd%gYEk#W+f`|ab##>VLAD0okK zl(sa`+uxItl7i2!udgpGEL<=-TkJGY(MFe&k^)xQ#`d<`?e)b#3Xh$mCTQ5$*tSP& z{f$oh5>~w`A>;;}=(g&$PC(VqjpPs;X-Kj}CrKV?2-?6dVk;QpQu= z={7NXYY7#ViTL>VKSykrwXrz_Uw?LX7FZ$+4C0i61|H9RD2KMh6&tN=%F}TR)`J8%NjsnIVnc zV)}asL9TR4L0;aEG++H%)Syt#aq0v~s==eXbA`m50#;*hUlkm(e*)lF;SB#@ zDmC6<<9#Z2oetE>PoEf}gEQ^1bm5te_W}?g2@B^>Fr(3~ovgbntWsY<&$@Y|`x@Oi z&>?ngCP$e4#1xuDYr>v>Bww(z1 zn_4R>^xWMs;cFh3pV%yu!vG!$=qfAG1WQ5W=AJj`$@!~GOLTxe%gW2kDk^R)w9Wt( zCJ!Ch>@!m*DzcGIc9pJox3G0hkS+w;X|YCOl}YB-`ntSkN`H*qO6vyS#Zsn=7s2_& zF<(cy!|6?+7u$(unD$I@QaE5TK!1MM>^d>2Y6ZS6pfy02$SVowvduz*%2dtqL*7_i zA1sapV59-TdqukFzfl7^1b7MtX-^vW*jK>czXc2uzvu>G`rzQbj*j!)>FSN?>I#=5 z_M4=|muB1iz$gL3p;62}q?!_}p{*^yw2G{=Blhd|CPCwMgVFCJ2?5U{&|RGDG~_ik6=c0RUOX$OT#d?upM2|F>AV>p8d^HtZ5>Q$wA9r# zH#O}rsE}8vG&h$7sQ{PxJP^J4`ipPO))o!YVQdlQpo;nK0xvF*(fMTxw36W9-y!%? zi`u<+ZqiCh+2FG7&rZF$nW;8AUD_(&TV7tiJ5vh+p5T)%i@CU5 ztPiG{y?4KN-~>*`q7tb&EPDX}iBGMkC^O|R8?8gD@Y2$-OBul_g%$J@q=@7%3c zejrHE(HZ{0#DRb=aJ%|>7ub~+I=#`+Zjg>aR!WKo9>lrodbqUvd$np^J## z4Gouo1ORZMt)%2|d2Hgt(es>&N?BRiHuqhVv(4$w6b%gxkmFvwdezt69TX8^^@VrV zO|8|_Lss@D=u)5!;Naqd2+_q_e^Oal89au{rf1e0l)>jP7|3=N6#)1q;5AXA31H7u zm5G9_?QhWUp)XBM?%zJ5gEMYEjKQD9g3YGkkt?a=x;;({%p)*SSh_u28uNlZ)(V4EZgjNllH-vSyM z8uIe;lnRx@NI0Yg1qHRV=D&Y`PD2x<5MlP(-TgX_P6J5e$dG2Eqtx5Cpv!}9*?RrE zcW0^+9*u^9f$`|k_Hed@e$O}ZYA0)t!G05757F5R)ct|Z|Ivpb4<$Xsyh#a@cR%7j z%N_=o;pXbB)%^kxh&e!F!QJE}_~^*a$!X%}Pv^T^zJrvoOfe9CZ9s(1kO9wK zTU!H{_8v0Y7y6xWQXYQy^D?Ev&Uvi%W&&EL*f_?4My*wm6<4D)yo2pI#P0#Vv$eJcW%v$&jrnFb6-C7=w==I!`g}(msBCHn zER>HQKFCD{CN(u(gEMF}knFNKob7e?{MXtHa*5J_7U#JzoU*marAD`073?~Ir2)VJ z)H~bL@H@^&*vV5fo1aaoIs3X6bGcatk75!u6zndCvloyb?~qq$QtE!8kdu}UZ*J!E z`&w36Ia8+VS5i^}pmEzS5!%OqfG$52M9VKI-s!>tj+?^(m&LkH0m?Ev`_420)?I5w zvZadqV|e&?M-a{|&D7KsIHwEl`h%%_j~+eZVgOeIATU+adaH#Ni|MNJiV9OxU5HZd zURL#7u)qJhFA=-Bs-q*j-`Ahx{x%CR%o-Ze5gPfE$C~9|C`>hjWW96zveSLjxZEc3VaUMrukMLg| z933?}Y~w?`05+}m#A<2L9fVK-Bn-nP<#8MZ)vLBPR|~*$jHbxzV(XgYBm{-Q0;{lj|ftp8s|ej!2a<=#%iq= zj*so79df|tahpu#sZH~|%r7YD0R^$50`vh7P%IR3Q0tQXHS}9X%exwgrq-&RYgjX~ zyVQFI7F##|w@_;hrBX0j(ujPj`M z?6kC73%K(FR);bsgrXT#-*m0~)t~(N;g23+1ENCz?~y{W!z8qTW-L=1!l1;DR|9eb z=%oMy`v1+=E3RC_`Fee{#KOkh3@}!{K!r)C2JyEM79&9L(Mz$1OR`DEgE|Pve^c zhMRjDKflv{oI()D{XQmP?`qTWO;BqC!UCtp!@jJRFz{|w)m?*$)lVTR$v{L%%h6M)%yAcu{c5%+7UA*ZdwIAZ zG}_({i|%_#H)muypHi9KCb>1k`O<7Gh8m^b2{>?|Bi0$q+=s6Lr%zTka$V~UT_~9V z(D*V-_Fly#kPvSz?pgMx&1{ZiUBS>Tsv6F?!Cqsf)|{DjXnNPUz_g;?18NPbJWx47P|?oX8Wk1w*TULpz5?j`!1XF(kcL+w zw}&YrXNHE_!Qo;G3It(KxV(cOgOAKfb5gBJgywtn-i19$Q!}MFBSR#5C4|6=WB{xd zARwNfF&p(^lakT_oDOmfV92w*6=5*9L$4fZjf#O`eb3SiBoAN)7CJiZz)ZcSLBvqF z3{(-2v$j3}HGgd&g77}~9Epvce z>c0(HzB_BQ_5BhP^F~sV_^)A_p&=9Cmm*fu(zkF}s22g(0|>HnP4${O!9`zko4PY`)Pw!S$&(t^0w0L@i3e>Kw zxB!QAeW2K|a!_vbnx5c>+9*cGje#m4 zVY0S(EKm91Z}X&ZILx$9lO5ZM`tdCye)r?Ut#V8D-x_CRA&b_|%e{VIQQ>?vK_WkO z7_q-Gj>7t1x=TrgRaDr1qp50Z3qFU#w?l;`+WFT2NIRxlmvP%kB;>E?qd}iaXAm`(aDDbI$e7-Jy4`C90)*!9~Lu}E3 z)$8U(iH1K(`1~1+OCDWc9083Uu?@6tpjOn^<`)*eq@h`qCIWg)>`RsC=x9i1yhv>% zN6PK`ygWQS{Dnj~CWlFgNE=>g7C#z-cV?zOfRST`Dga`E;ts?yHCfqTzkanf_xB~R zfUQ@r0N`^@adE!^Gr}(*#?Q~sL%&v6pZ7#lc1XEi2Gw~~-qSEeBWBI3s$vDQ-So7& znwksf*yb$H93`HXJ^mP&xa!8s9q{8K4XGyf%xr>P6Pka5lj`}7mz^n@VHSfR%cLO z0e1&L<^OYE03h}ijQud2yo#31ij_4_)#A z=o%K*k0Ed^tEOgRU;rBA^BumLABK!JOBJD^vzsGbXheFpAa+n~hM}%{{DSr^1?mjo zjoV3E2T5i;_U}nZcU81*Z9x>7&y9Gtq56oYcgojV857nooywaKAFryR0oF7!3kyp_ z$cQS4i6eV|ctj0i&%8`Foed5wnwlsWM9lY~y}3d{Hd9?VJR#e#tfHbhI=VSQ?X{;T z&Dk^{o?xhZy2?gg!S3`-`+EYByl`f9T3T;~AwP(WfSLVYW(U6?KGtG!H17Z@7FXA& z4u{7Yp}Q|U@(H38#}cP14|icJMYMO*Wl zpIt8w78QV<;&pR!{UhoTNRRmV97V@OfaruU7#i4ncLuJ*bBP)-9HD1kbk@tD02WnR zjNQrg#jxd^J%F-9H5QNFa|>f5Q$`hyjE}dtUog*QzpntH082|@ASje-HiFl)(QLjK z?X1rf(}!Ne;esl)H}!UAEG&;o-_?W4!v>w7m#b^H-ZQrF^xXW1;h|{xCs^^p?Tj{W zo&wD4Y)=ekV1pU>ii#za$7sm^Q-q<;LZhzU1q%62|6&r%L2x=2Ie-ZovG8|{Oq&@X zuWvakE0;#x?GN&1S?A}?Ag&!0fsxGXaz6wwub6LdG@oJ*9r2!u)j}!}-fHvvA>33Y z=*z#y9ZbLeTR!4qyZQ0fRxx2{=?!6r7a0m71boOXW#@u7088)eS-OEcCoFii67sp0 zHE{3K;fg;4E-v;a7%G?|Feh47)TlBY_VM*7mteNIJ zWIpGE@=AXAQmrxCg{vV-KqmDYZ^T z<>jf_hBDd<=Q3%gkj~aMkRggQrBk{W)n|DVSLpl}~dOAPlzy^#Nd99m< zk}tUag5UV(Cs;Sgs{!@X8O(1P4;1CEH`f51+GwpS{Fy$*Ij+9TkmPBOg?D-R1h@l% z;vsQNkUd8J!&uh2zONCm?g+9%I=Wm9jWf7(>Vz+s#MyH(yX{b7){`TmAs1OZcMz0HSL zM#k;s%b4I`6<(fIwBx<`W*Fa%xn$p7oPa0HK~X7oZ%)GP;&)k45c!;86pS?@MNSwG z1_x6kNlf~hphN_sB9O9zi713Wn^UIrUTk}_q@fVq*#;9HbS`Fn*Pr|MY|@V&ATEv- z=7L*yOgCLjf=pPQllsDI1|f7EMnW(ZF|7HpTi7lokhoxDNO)ml0ttHjkQ$5}o-3$PCfUqt-IU{peA6P&@cz}kS8FjBYFRH_s>K=^hp!U2U zHYZ#l^jzw*&kOk3^MEr|%Kz=wHW^R9cP(o8V+y#}h_UF3-#m3b#l%Ri#2R{S+7c-O zZ}`S>`hVTK=3n>4asDez;w&Kdum1@$Cqcw#pH%;m90|ezRpy^l<6wZ?KDtFsR_;hA zQ#{cte{yW;Ta0j&z WQDI1MrAYxO0m0u$i
Artifact Lumiera

Depends on common

Depends on gui

Depends on proc

Depends on backend

the main executable to be built

-

executable associated with : mobject, source, frame, placement, sessionimpl, builderfacade, controllerfacade, processor, pluginadapter, effect, buildertool, segmentationtool, aframe, assembler, trafo, explicitplacement, auto, glrender, link, parameter, renderengine, allocation, vframe, toolfactory, arender, renderstate, label, glbuf, procnode, stateproxy, hub, buildable, abstractmo, nodecreatertool, projector, interpolator, edl, fixture, glpipe, vrender, exitnode, pathmanager, track, paramprovider, mask, main, conmanager, clip, meta, fixedlocation, relativelocation

+

executable associated with : exitnode, pathmanager, track, paramprovider, mask, main, conmanager, clip, meta, fixedlocation, relativelocation, mobject, source, frame, placement, sessionimpl, builderfacade, controllerfacade, processor, pluginadapter, effect, buildertool, segmentationtool, aframe, assembler, trafo, explicitplacement, auto, glrender, link, parameter, renderengine, allocation, vframe, toolfactory, arender, renderstate, label, glbuf, procnode, stateproxy, hub, buildable, abstractmo, nodecreatertool, projector, interpolator, edl, fixture, glpipe, vrender

Artifact main

Artifact source

@@ -902,12 +902,16 @@ reuse exiting Engine

Selection :

    Transformation
    -
    -
    Class Caching
    -
    Class Process
    -
    Class InPlace
    -
    +
    +
    +
    Class PullInput
    +
    +
    +
    Class FeedCache
    +
    Class Strategy
    +
    Class BuffTable
    +

    diff --git a/doc/devel/uml/index_60.html b/doc/devel/uml/index_60.html index 8712d7074..a873c25b0 100644 --- a/doc/devel/uml/index_60.html +++ b/doc/devel/uml/index_60.html @@ -17,8 +17,8 @@ - + @@ -28,10 +28,10 @@ - - + + diff --git a/doc/devel/uml/index_65.html b/doc/devel/uml/index_65.html index ab57596c5..33d5ebd2f 100644 --- a/doc/devel/uml/index_65.html +++ b/doc/devel/uml/index_65.html @@ -55,12 +55,12 @@ - + - - + + diff --git a/doc/devel/uml/index_66.html b/doc/devel/uml/index_66.html index db64b33db..3a6ba7379 100644 --- a/doc/devel/uml/index_66.html +++ b/doc/devel/uml/index_66.html @@ -24,6 +24,7 @@ + @@ -36,8 +37,8 @@ - + diff --git a/doc/devel/uml/index_67.html b/doc/devel/uml/index_67.html index 1612ca9ea..8432e5cc6 100644 --- a/doc/devel/uml/index_67.html +++ b/doc/devel/uml/index_67.html @@ -19,7 +19,8 @@ - + + @@ -29,34 +30,34 @@ - - - - - - - + + + + + - - - + + + + + + + - - - - - - - - - - + + + + + + + + diff --git a/doc/devel/uml/index_69.html b/doc/devel/uml/index_69.html index 1f6fd3f19..a7245b34b 100644 --- a/doc/devel/uml/index_69.html +++ b/doc/devel/uml/index_69.html @@ -18,14 +18,14 @@
    NameKindDescription
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    <flow>transition
    Assetsclass view
    ATTACHattributeattach subject to anchor (e.g. an effect to a clip)
    au1class instance
    aud_aclass instance
    aud_Aclass instance
    aud_aclass instance
    audioclass instance
    audio1class instance
    audio1class instance
    audio1class instance
    audio1class instance
    audio1class instance
    autoartifactMedia Object holding automation data
    AutoclassAutomation data for some parameter (i.e. a time varying function)
    Automation Entitiesclass diagram
    BackendCacheclass
    BackendLayerpackage
    BuffHandleclass
    BuffTableclass
    buildoperation
    build flowactivity diagram
    build necessary?decision activity node
    buildableartifactmarker interface denoting any MObject able to be treated by Tools
    buildEngineoperationMain Operation of the Builder: create a render engine for a given part of the timeline
    Buildercomponent
    builderpackagesourcecode package

    The Builder creating the Render Engine,
    located within the MObject Subsystem
    Builderpackage
    builderpackagesourcecode package

    The Builder creating the Render Engine,
    located within the MObject Subsystem
    Builder Entitiesclass diagram
    Builder Workingsclass view
    BuilderFacadeclassProvides unified access to the builder functionality. While individual components of the builder subsystem may be called if necessary or suitable, it is usually better to do all extern invocations via the high level methods of this Facade
    NameKindDescription
    Cachecomponent
    Cachecomponent view
    Cachingclass
    callDownoperation
    callDownoperation
    Callerclass
    categoryrelationprimary tree like classification of the asset
    Categoryclasstree like classification of Assets
    chainrelationChain of additional Placements further constraining the position of this MObject
    checked_inrelationchecked_in objects are subject of cache aging and must be not in use
    checked_outrelationthis list keeps all mappings which are in use, and thus prevents them from Cache aging
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    class instanceclass instance
    clearoperationclear current session contents
    without resetting overall session config.
    Afterwards, the session will contain only one
    empty EDL, while all Assets are retained.
    client codecomponent
    Clipclassbookkeeping (asset) view of a media clip.
    - + - + diff --git a/doc/devel/uml/index_70.html b/doc/devel/uml/index_70.html index 5e1189dd5..56bc1daaf 100644 --- a/doc/devel/uml/index_70.html +++ b/doc/devel/uml/index_70.html @@ -18,6 +18,7 @@
    NameKindDescription
    edlartifactthe (high level) Edit Decision List within the current Session
    EDLclass
    EDLcomponent
    EDLclass
    EDL Example1object diagramA simple example showing how the actual objects are placed in the Fixture (=definitive playlist). It shows a Video and Audio clip placed on two tracks
    EDL Example2object diagramMore complex example showing the Object graph in the EDL and how it is linked into the Fixture to yield the actual locations. In this example, an HUE Effect is applied on a part of the Clip
    edlsrelation
    EffectclassEffect or media processing component
    effectartifactEffect or media processing component
    effectartifactEDL representation of a pluggable and automatable effect.
    effectartifactEffect or media processing component
    Effectclass
    elementsrelationrelevant MObjects comprising this segment. TODO: actually necessary??
    enableoperationchange the enabled status of this asset. Note the corresponding #isActive predicate may depend on the enablement status of parent assets as well
    + diff --git a/doc/devel/uml/index_72.html b/doc/devel/uml/index_72.html index 3547e6f3e..7e5b73a00 100644 --- a/doc/devel/uml/index_72.html +++ b/doc/devel/uml/index_72.html @@ -23,8 +23,8 @@ - +
    NameKindDescription
    Factoryclassa template for generating functor-like Factory objects, used to encapsulate object creation and providing access via smart-pointers only.
    FeedCacheclass
    fetchoperation
    Fileclass
    filerelation
    Hierarchyclass diagramLumiera Exception hierarchy
    howtoProcoperation@return descriptor how to build a render pipeline corresponding to this media
    hubartifactspecial ProcNode used to build data distributing connections
    HUEclass instance
    HUEclass instance
    HUEclass instance
    diff --git a/doc/devel/uml/index_73.html b/doc/devel/uml/index_73.html index 9a0a77429..e127cf07e 100644 --- a/doc/devel/uml/index_73.html +++ b/doc/devel/uml/index_73.html @@ -20,10 +20,9 @@ idattributeAsset primary key. In Memory Databaseclass diagram inFixtureactivity action pin -InPlaceclass -inputclass instance -inputclass instance inputclass instance +inputclass instance +inputclass instance instanceoperation instructionsrelation Interfaceclass view @@ -32,7 +31,7 @@ interpolatorartifactdenotes a facility to get (continuously interpolated) parameter values InterpolatorclassProvides the implementation for getting the acutal value of a time varying or automated effect/plugin parameter Invalidclass -InvocationStateBaseclass +Invocationclass iporelation isActiveoperationweather this asset is swithced on and consequently included in the fixture and participates in rendering isCalculatedoperation diff --git a/doc/devel/uml/index_77.html b/doc/devel/uml/index_77.html index 1428ef6eb..e6d8e8031 100644 --- a/doc/devel/uml/index_77.html +++ b/doc/devel/uml/index_77.html @@ -33,8 +33,8 @@ MediaFactoryclassspecialized Asset Factory for configuring (new) media asset instances based on existing media files on disk; can create placeholder assets as well merge activity nodemerge activity node Metaclasskey abstraction: metadata and organisational asset -metaartifactabstract base class of all MObjects representing meta data or processing instructions metaartifactkey abstraction: metadata and organisational asset +metaartifactabstract base class of all MObjects representing meta data or processing instructions Metaclass mobjectartifactKey Abstraction: A Media Object in the Session mobjectpackagesourcecode package

    MObject Subsystem
    including the Session (EDL), Builder and Processing Controller diff --git a/doc/devel/uml/index_79.html b/doc/devel/uml/index_79.html index d1fdc83c0..e17eadde8 100644 --- a/doc/devel/uml/index_79.html +++ b/doc/devel/uml/index_79.html @@ -18,6 +18,7 @@ + diff --git a/doc/devel/uml/index_80.html b/doc/devel/uml/index_80.html index a3268b59b..368babe6a 100644 --- a/doc/devel/uml/index_80.html +++ b/doc/devel/uml/index_80.html @@ -53,8 +53,8 @@ - + @@ -67,6 +67,7 @@ +
    NameKindDescription
    offsetattributeOffset the actual position by this (time) value relative to the anchor point. TODO: Representation?
    OperationBaseclass
    orgattributeorigin or authorship id. Can be a project abbreviation, a package id or just the authors nickname or UID. This allows for the compnent name to be more generic (e.g. "blur"). Default for all assets provided by the core Lumiera codebase is "lumi".
    ouputclass instance
    ouputclass instance
    Proc-Asset Relationsclass diagram
    proc-componentscomponent diagram
    ProcDispatcherclass
    Processclass
    processoperation
    ProcessDataclass
    ProcessingLayerpackage
    Processorclass
    processorartifacta single render pipeline for one segment of the timeline
    projectorartifactvideo ProcNode for scaling and translating image data
    providerrelation
    pulloperation
    PullInputclass
    diff --git a/doc/devel/uml/index_81.html b/doc/devel/uml/index_81.html index 5a521bd2f..6367201fd 100644 --- a/doc/devel/uml/index_81.html +++ b/doc/devel/uml/index_81.html @@ -20,6 +20,7 @@ queryclass view Query System overviewcomponent view query useuse case view +QueryCacheclass QueryHandlerclass QueryHandlerImplclass diff --git a/doc/devel/uml/index_82.html b/doc/devel/uml/index_82.html index b5b305e10..9b107d854 100644 --- a/doc/devel/uml/index_82.html +++ b/doc/devel/uml/index_82.html @@ -17,14 +17,15 @@ + - + diff --git a/doc/devel/uml/index_83.html b/doc/devel/uml/index_83.html index 5dd747140..4ea8bf79b 100644 --- a/doc/devel/uml/index_83.html +++ b/doc/devel/uml/index_83.html @@ -32,8 +32,8 @@ - + @@ -47,8 +47,8 @@ - + @@ -64,6 +64,7 @@ + diff --git a/doc/devel/uml/index_84.html b/doc/devel/uml/index_84.html index b0541e88b..96532f18f 100644 --- a/doc/devel/uml/index_84.html +++ b/doc/devel/uml/index_84.html @@ -42,13 +42,13 @@ - + - +
    NameKindDescription
    ReadSourceclass
    refPointclass instance
    regoperationregisters an asset object in the internal DB, providing its unique key
    registryrelation@internal Table or DB holding all registered asset instances.
    relativelocationartifactPlacement implemnetaion providing various ways of attaching a MObject to another one
    RelativeLocationclass
    releaseBufferoperation
    relTypeattributethe kind of relation denoted by this Placement
    RelTypeclassthe possible kinds of RelativePlacements
    relTypeattributethe kind of relation denoted by this Placement
    removeoperationremove the given asset <i>together with all its dependants</i> from the internal DB
    Render Entitiesclass diagram
    Render Mechanicsclass diagram
    Service Componentsclass view
    Sessioncomponent
    sessionartifactInterface: the session edited by the user
    sessionpackagesourcecode package

    Everything concerning the EDL and Session, within the MObject Subsystem
    Sessionclass view
    sessionpackagesourcecode package

    Everything concerning the EDL and Session, within the MObject Subsystem
    SessionclassPrimary Interface for all editing tasks.
    The session contains defaults, all the assets being edited, and a set of EDL with the individual MObjects to be manipulated and rendered.
    Session structureclass diagram
    sessionimplartifactholds the complete session data to be edited by the user
    SimpleClipclassElementary clip consisting of only one media stream
    SmartPointerclass
    SmartPointersclass view
    sourcerelationmedia source of this clip
    sourcerelationthe media source this clip referes to
    sourcerelationmedia source of this clip
    SourceclassSource Node: represents a media source to pull data from.
    sourceartifactRepresentation of a Media source
    Source Overviewdeployment diagram
    StateProxyclass
    stateproxyartifactKey Interface representing a render process and encapsulating state
    std::exceptionclass
    Strategyclass
    Structclasskey abstraction: structural asset
    structartifactkey abstraction: structural asset
    Struct-Asset Relationsclass diagram
    trafoartifacttransforming processing Node
    treatoperation
    treatoperationThis operation is to be overloaded for the specific MObject subclasses to be treated.
    treatoperation
    treatoperation
    treatoperation
    treatoperation
    treatoperation
    treatoperation
    treatoperation
    treatoperation
    treatoperation
    TypeHandlerclass
    TypeHandler<Pipe>class
    diff --git a/doc/devel/uml/index_86.html b/doc/devel/uml/index_86.html index cda6a585e..3538c8202 100644 --- a/doc/devel/uml/index_86.html +++ b/doc/devel/uml/index_86.html @@ -22,18 +22,18 @@ vframeartifacta buffer and render process holding a Video frame vid1class instance vid_Aclass instance -vid_aclass instance +vid_aclass instance vid_Aclass instance vid_Aclass instance -vid_aclass instance -videoclass instance -videoclass instance +vid_aclass instance videoclass instance +videoclass instance videoclass instance +videoclass instance video1class instance -video1class instance video1class instance video1class instance +video1class instance video1class instance video1class instance Visitableclass diff --git a/doc/devel/uml/index_87.html b/doc/devel/uml/index_87.html index 7e2b7e31f..616839a54 100644 --- a/doc/devel/uml/index_87.html +++ b/doc/devel/uml/index_87.html @@ -20,9 +20,7 @@ whatoperation whatoperationthe base class of all exceptions thrown by the standard library when to queryuse case diagram -wiringrelation WiringDescriptorclass -WiringTableclass wiringTemplaterelation wishartifactLocatingPin representing a low-priority directive by the user,
    to be fulfilled only if possible (and after satisfying the
    more important LocatingPins) Wishclass diff --git a/doc/devel/uml/public_operations.html b/doc/devel/uml/public_operations.html index c252030f0..07fb079d7 100644 --- a/doc/devel/uml/public_operations.html +++ b/doc/devel/uml/public_operations.html @@ -23,6 +23,8 @@ buildAssembler buildEngineBuilderFacadeMain Operation of the Builder: create a render engine for a given part of the timeline buildProcessorPathManager +callDownNodeWiring +callDownWiringDescriptor chainPlacementcreate and add another Placement for this media object, thus increasingly constraining the (possible) position of this object. clearSessManagerclear current session contents
    without resetting overall session config.
    Afterwards, the session will contain only one
    empty EDL, while all Assets are retained.
    configureToolFactory @@ -67,12 +69,12 @@ saveSessManagercreate a complete, serialized representation
    of the current session config and contents.
    @todo how to serialize, prameters, return value? treatApplicable treatBuilderToolThis operation is to be overloaded for the specific MObject subclasses to be treated. -treatNodeCreatorTool treatNodeCreatorTool treatNodeCreatorTool +treatNodeCreatorTool treatNodeCreatorTool -treatSegmentationTool treatSegmentationTool +treatSegmentationTool treatSegmentationTool useFileFileProviderAnnounces that the application intends to use this file with mode (READ|WRITE|READWRITE) useTemporaryStorageFileProviderProvides a pool for interminate frames diff --git a/src/common/util.hpp b/src/common/util.hpp index 7f8704554..c71ba3b6a 100644 --- a/src/common/util.hpp +++ b/src/common/util.hpp @@ -237,4 +237,8 @@ namespace util * and we probably will enforce this design rule in future. */ #define INSTANCEOF(CLASS, EXPR) (dynamic_cast (EXPR)) +/** the inevitalbe MAX macro, sometimes still necessary in template code */ +#define MAX(A,B) (A < B) ? (B) : (A) + + #endif /*UTIL_HPP_*/ diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index 2cd5eb09d..c8740def0 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -81,34 +81,86 @@ namespace engine { * defined in nodewiring.cpp actually drives the instantiation of all * those possible combinations */ - class InvocationStateBase + class StateAdapter : public State { - WiringDescriptor const& wiring_; - protected: State& parent_; State& current_; uint requiredOutputNr; - StateAdapter (State& callingProcess, WiringDescriptor const&) + StateAdapter (State& callingProcess) : parent_ (callingProcess), current_(callingProcess.getCurrentImplementation()) { } - friend class NodeWiring; // both are sharing implementation details... - - virtual State& getCurrentImplementation () { return current_; } }; + + struct BuffTable + { + BuffHandle *const outHandle; + BuffHandle *const inHandle; + BuffHandle::PBuff *const outBuff; + BuffHandle::PBuff *const inBuff; + }; + + + struct Invocation + { + WiringDescriptor const& wiring; + BuffTable& buffTab; + const uint outNr; + + protected: + Invocation (WiringDescriptor const& w, BuffTable& b, uint o) + : wiring(w), + buffTab(b), + outNr(o) + { } + }; + + + template + class InvocationImpl + : private StateAdapter, + private BuffTableStorage, + public Invocation + { + + protected: + InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh) + : StateAdapter(callingProcess), + BuffTableStorage(w), + Invocation(w, static_cast(*this), outCh) + { } + + /** contains the details of Cache query and recursive calls + * to the predecessor node(s), eventually followed by the + * ProcNode::process() callback + */ + template + BuffHandle retrieve () + { + return Strategy::step (*this); + } + }; + + + + + class OperationBase + { + + }; template struct QueryCache : NEXT { BuffHandle - step () + step (Invocation& ivo) { BuffHandle fetched = this->current_.fetch ( this->genFrameID ( @@ -116,7 +168,7 @@ namespace engine { if (fetched) return fetched; else - return NEXT::step(); + return NEXT::step (ivo); } }; @@ -125,7 +177,7 @@ namespace engine { struct PullInput : NEXT { BuffHandle - step () + step (Invocation& ivo) { this->createBuffTable(); @@ -143,7 +195,7 @@ namespace engine { *(inH[i] = this->pullPredecessor(i)); // invoke predecessor // now Input #i is ready... } - return NEXT::step(); + return NEXT::step (ivo); } }; @@ -152,7 +204,7 @@ namespace engine { struct ReadSource : NEXT { BuffHandle - step () + step (Invocation& ivo) { this->createBuffTable(); @@ -171,7 +223,7 @@ namespace engine { *(inH[i] = outH[i] = this->getSource(i)); // TODO: how to access source nodes??? // now Input #i is ready... } - return NEXT::step(); + return NEXT::step (ivo); } }; @@ -180,7 +232,7 @@ namespace engine { struct AllocOutput { BuffHandle - step () + step (Invocation& ivo) { ASSERT (this->buffTab); ASSERT (this->nrO < this->buffTabSize()); @@ -193,7 +245,7 @@ namespace engine { *(outH[i] = allocateBuffer (i)); // now Output buffer for channel #i is available... } - return NEXT::step(); + return NEXT::step (ivo); } private: @@ -228,7 +280,7 @@ namespace engine { struct ProcessData { BuffHandle - step () + step (Invocation& ivo) { ASSERT (this->buffTab); ASSERT (this->nrO+this->nrI <= this->buffTabSize()); @@ -237,7 +289,7 @@ namespace engine { // Invoke our own process() function, providing the buffer array this->wiring_.processFunction (this->buffTab->buffers); - return NEXT::step(); + return NEXT::step (ivo); } }; @@ -245,13 +297,13 @@ namespace engine { struct FeedCache { BuffHandle - step () + step (Invocation& ivo) { // declare all Outputs as finished this->current_.isCalculated(this->buffTab->handles, this->nrO); - return NEXT::step(); + return NEXT::step (ivo); } }; @@ -259,7 +311,7 @@ namespace engine { struct ReleaseBuffers { BuffHandle - step () + step (Invocation& ivo) { // all buffers besides the required Output no longer needed this->current_.releaseBuffers(this->buffTab->handles, @@ -270,28 +322,6 @@ namespace engine { } }; - template - struct NoProcess - { - BuffHandle calculateResult(BuffHandle* calculated) - { - uint nrO = this->getNrO(); - for (uint i = 0; iretrieveInput(i); ///TODO: Null pointer when no caching!!!!! - this->outBuff[i] = current_.getBuffer(calculated[i]); - // now Buffer containing Output channel #i is available... - } - - this->feedCache(); - for (uint i=0; i < nrO; ++i) - if (i!=requiredOutputNr) - current_.releaseBuffer(i); - - return calculated[requiredOutputNr]; - }; - - }; @@ -314,13 +344,13 @@ namespace engine { template struct Strategy< Config > - : QueryCache < + : QueryCache< PullInput< AllocOutput, ProcessData< FeedCache< ReleaseBuffers< - InvocationStateBase > > > > > > + OperationBase > > > > > > { }; template @@ -329,14 +359,14 @@ namespace engine { AllocOutput, ProcessData< ReleaseBuffers< - InvocationStateBase > > > > + OperationBase > > > > { }; template<> struct Strategy< Config<> > : ReadSource< ReleaseBuffers< - InvocationStateBase > > + OperationBase > > { }; template<> @@ -348,28 +378,10 @@ namespace engine { AllocOutput, ProcessData< // wiring_.processFunction is supposed to do just buffer copying here ReleaseBuffers< - InvocationStateBase > > > > + OperationBase > > > > { }; - template - class StateAdapter - : Strategy - { - - protected: - - /** contains the details of Cache query and recursive calls - * to the predecessor node(s), eventually followed by the - * ProcNode::process() callback - */ - BuffHandle retrieve (uint outNr) - { - this->requiredOutputNr = outNr; - return Strategy::step (); - } - - }; } // namespace engine diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index aba017fde..1fc221bf6 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -25,6 +25,7 @@ #define ENGINE_NODEWIRINGCONFIG_H +#include "common/util.hpp" //#include #include @@ -121,11 +122,11 @@ namespace lumiera { { }; template class _PERM_> struct Combine,_PERM_> - : Distribute<_PERM_, Combine > + : Distribute<_PERM_, Combine > { }; template - struct FlagOnOff : Types::List + struct FlagOnOff : Types::List { }; template @@ -168,11 +169,11 @@ namespace lumiera { template struct DefineConfigByFlags; - template< char f1=0 - , char f2=0 - , char f3=0 - , char f4=0 - , char f5=0 + template< char f1 + , char f2 + , char f3 + , char f4 + , char f5 > struct DefineConfigByFlags< FlagTuple > : Config @@ -199,7 +200,7 @@ namespace lumiera { template struct FlagInfo, FLAGS> > { - enum{ BITS = max(ff, FlagInfo::BITS) + enum{ BITS = MAX(ff, FlagInfo::BITS) , CODE = (1<::CODE }; }; @@ -211,7 +212,7 @@ namespace lumiera { }; template - static FUNC::Ret + static typename FUNC::Ret accept (FUNC& functor) { return functor.done(); @@ -223,11 +224,11 @@ namespace lumiera { > > { typedef Node, FLAGS> ThisConfig; - enum{ BITS = max (FlagInfo::BITS, FlagInfo::BITS) + enum{ BITS = MAX (FlagInfo::BITS, FlagInfo::BITS) }; template - static FUNC::Ret + static typename FUNC::Ret accept (FUNC& functor) { functor.template visit(FlagInfo::CODE); @@ -271,14 +272,6 @@ namespace engine { typedef std::bitset Bits; - //////////////////////////////////TODO: noch nötig?? - template - Bits - getCaseRepresentation (CONF config) - { - return Bits (FlagInfo::CODE); - } - /** @@ -325,24 +318,25 @@ namespace engine { * the use of template metaprogramming for extracting all * currently defined StateProxy object configurations. */ - template class Factory> + template< template class Factory + , class PAR ///< ctor parameter of the Factories + , class RET ///< common base class of the Factory's products + > class ConfigSelector { - typedef typename Factory::Ret Ret; - typedef typename Factory::Param Param; struct FacFunctor { virtual ~FacFunctor() {} - virtual Ret invoke() =0; + virtual RET invoke() =0; }; template struct FactoryHolder : FacFunctor { FAC factory_; - FactroyHolder(Param p) : factory_(p) {} + FactoryHolder(PAR p) : factory_(p) {} - virtual Ret invoke () { return factory_(); } + virtual RET invoke () { return factory_(); } }; @@ -354,10 +348,10 @@ namespace engine { /** Helper: a visitor usable with FlagInfo */ struct FactoryTableBuilder { - Param ctor_param_; + PAR ctor_param_; ConfigTable& factories_; - FactoryTableBuilder (ConfigTable& tab, Param p) + FactoryTableBuilder (ConfigTable& tab, PAR p) : ctor_param_(p), factories_(tab) { } @@ -370,7 +364,7 @@ namespace engine { void visit (ulong code) { - typedef typename DefineConfigByFlags Config; + typedef DefineConfigByFlags Config; factories_[code].reset (new FactoryHolder > (ctor_param_)); } @@ -379,7 +373,7 @@ namespace engine { public: template - ConfigSelector(Param factory_ctor_param) + ConfigSelector(PAR factory_ctor_param) { FactoryTableBuilder buildTable(this->possibleConfig_, factory_ctor_param ); @@ -388,7 +382,7 @@ namespace engine { FlagInfo::accept (buildTable); } - Ret + RET operator() (Bits configFlags) ///< invoke the factory corresponding to the given config { return possibleConfig_[configFlags]->invoke(); diff --git a/src/proc/state.hpp b/src/proc/state.hpp index 7828398ed..2f047ee7a 100644 --- a/src/proc/state.hpp +++ b/src/proc/state.hpp @@ -34,7 +34,7 @@ namespace engine { - class StateAdapter; + class InvocationStateBase; struct BuffHandle { @@ -72,7 +72,7 @@ namespace engine { */ virtual State& getCurrentImplementation () =0; - friend class engine::StateAdapter; + friend class engine::InvocationStateBase; public: diff --git a/uml/lumiera/128389 b/uml/lumiera/128389 index 2a8c1a7c0..05bf51f89 100644 --- a/uml/lumiera/128389 +++ b/uml/lumiera/128389 @@ -1,6 +1,6 @@ format 40 "RenderEngine" // ProcessingLayer::RenderEngine - revision 16 + revision 17 modified_by 5 "hiv" // class settings //class diagram settings @@ -517,11 +517,6 @@ ${inlines} class 142469 "StateProxy" visibility package stereotype "implementation" - nformals 2 - formal name "WIRING" type "class" explicit_default_value "" - explicit_extends "" - formal name "POLICY" type "class" explicit_default_value "" - explicit_extends "" cpp_decl "${comment}${template}class ${name}${inherit} { ${members} }; @@ -539,22 +534,6 @@ ${inlines} b multiplicity "" parent class_ref 132741 // State end - classrelation 153221 // - relation 149765 ---|> - a public - cpp default "${type}" - classrelation_ref 153221 // - b multiplicity "" parent class_ref 143877 // InPlace - end - - classrelation 154117 // wiring () - relation 150661 ---> - a role_name "wiring" multiplicity "1" const_relation protected - cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; -" - classrelation_ref 154117 // wiring () - b multiplicity "" parent class_ref 144005 // WiringTable - end end class 142597 "StateAdapter" @@ -621,12 +600,30 @@ ${inlines} idl_decl "" explicit_switch_type "" + operation 136453 "callDown" + cpp_virtual public explicit_return_type "" + nparams 2 + param inout name "" type class_ref 132741 // State + param in name "requiredOutput" explicit_type "uint" + nexceptions 1 + exception class_ref 144133 // BuffHandle + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + end end class 143365 "NodeWiring" visibility package nformals 1 - formal name "STATE" type "class" explicit_default_value "" + formal name "CONF" type "class" explicit_default_value "" explicit_extends "" cpp_decl "${comment}${template}class ${name}${inherit} { @@ -648,13 +645,74 @@ ${inlines} classrelation 153093 // relation 149637 -_-> a default - cpp default "Generated" + cpp default "#include in header" classrelation_ref 153093 // b multiplicity "" parent class_ref 142597 // StateAdapter end + + classrelation 156165 // + relation 152581 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156165 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156293 // + end + + classrelation 156421 // + relation 152709 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156421 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156549 // + end + + classrelation 156677 // + relation 152837 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156677 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156805 // + end + + classrelation 156933 // + relation 152965 ---> + stereotype "creates" + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156933 // + b multiplicity "" parent class_ref 144261 // Invocation + end + + operation 136581 "callDown" + public explicit_return_type "" + nparams 0 + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + end end - class 143493 "InvocationStateBase" + class 143493 "OperationBase" visibility package cpp_decl "${comment}${template}class ${name}${inherit} { @@ -665,16 +723,9 @@ ${inlines} idl_decl "" explicit_switch_type "" - classrelation 153733 // - relation 150277 ---|> - a public - cpp default "${type}" - classrelation_ref 153733 // - b multiplicity "" parent class_ref 132741 // State - end end - class 143621 "Caching" + class 143621 "QueryCache" visibility package cpp_decl "${comment}${template}class ${name}${inherit} { @@ -685,16 +736,16 @@ ${inlines} idl_decl "" explicit_switch_type "" - classrelation 153605 // - relation 150149 ---|> + classrelation 157189 // + relation 153221 ---|> a public cpp default "${type}" - classrelation_ref 153605 // - b multiplicity "" parent class_ref 143493 // InvocationStateBase + classrelation_ref 157189 // + b multiplicity "" parent class_ref 143749 // PullInput end end - class 143749 "Process" + class 143749 "PullInput" visibility package cpp_decl "${comment}${template}class ${name}${inherit} { @@ -705,16 +756,28 @@ ${inlines} idl_decl "" explicit_switch_type "" - classrelation 153477 // - relation 150021 ---|> + classrelation 157317 // + relation 153349 ---|> a public cpp default "${type}" - classrelation_ref 153477 // - b multiplicity "" parent class_ref 143621 // Caching + classrelation_ref 157317 // + b multiplicity "" parent class_ref 144389 // ProcessData + end + + classrelation 158085 // + relation 154117 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 158085 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 158213 // end end - class 143877 "InPlace" + class 143877 "ReadSource" visibility package cpp_decl "${comment}${template}class ${name}${inherit} { @@ -725,16 +788,119 @@ ${inlines} idl_decl "" explicit_switch_type "" - classrelation 153349 // - relation 149893 ---|> + classrelation 157701 // + relation 153733 ---|> a public cpp default "${type}" - classrelation_ref 153349 // - b multiplicity "" parent class_ref 143749 // Process + classrelation_ref 157701 // + b multiplicity "" parent class_ref 143493 // OperationBase end end - class 144005 "WiringTable" + class 144389 "ProcessData" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 157445 // + relation 153477 ---|> + a public + cpp default "${type}" + classrelation_ref 157445 // + b multiplicity "" parent class_ref 144645 // FeedCache + end + + classrelation 157829 // + relation 153861 ---|> + a public + cpp default "${type}" + classrelation_ref 157829 // + b multiplicity "" parent class_ref 143493 // OperationBase + end + + classrelation 158213 // + relation_ref 154117 // + end + end + + class 144645 "FeedCache" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 157573 // + relation 153605 ---|> + a public + cpp default "${type}" + classrelation_ref 157573 // + b multiplicity "" parent class_ref 143493 // OperationBase + end + end + + class 144517 "Strategy" + visibility package + nformals 1 + formal name "CONF" type "class" explicit_default_value "" + explicit_extends "" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 154757 // + relation 151301 ---|> + a public + cpp default "${type}" + classrelation_ref 154757 // + b multiplicity "" parent class_ref 143877 // ReadSource + end + + classrelation 156293 // + relation_ref 152581 // + end + + classrelation 156549 // + relation_ref 152709 // + end + + classrelation 156805 // + relation_ref 152837 // + end + + classrelation 157061 // + relation 153093 ---|> + a public + cpp default "${type}" + classrelation_ref 157061 // + b multiplicity "" parent class_ref 143621 // QueryCache + end + + classrelation 157957 // + relation 153989 ---|> + a public + cpp default "${type}" + classrelation_ref 157957 // + b multiplicity "" parent class_ref 143749 // PullInput + end + end + + class 144005 "BuffTable" visibility package nformals 1 formal name "SIZ" type "int" explicit_default_value "" @@ -780,5 +946,44 @@ ${inlines} explicit_switch_type "" end + + class 144261 "Invocation" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 155781 // + relation 152197 ---> + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 155781 // + b multiplicity "" parent class_ref 142597 // StateAdapter + end + + classrelation 155909 // + relation 152325 ---> + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 155909 // + b multiplicity "" parent class_ref 142725 // WiringDescriptor + end + + classrelation 156037 // + relation 152453 ---> + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 156037 // + b multiplicity "" parent class_ref 144005 // BuffTable + end + end end end diff --git a/uml/lumiera/128901 b/uml/lumiera/128901 index 455fee7d7..9a2bf5b90 100644 --- a/uml/lumiera/128901 +++ b/uml/lumiera/128901 @@ -1,6 +1,6 @@ format 40 "Builder" // ProcessingLayer::MObject::Builder - revision 16 + revision 17 modified_by 5 "hiv" // class settings //class diagram settings diff --git a/uml/lumiera/129285 b/uml/lumiera/129285 index 246c9e4d6..1441c4bdf 100644 --- a/uml/lumiera/129285 +++ b/uml/lumiera/129285 @@ -1,6 +1,6 @@ format 40 "ProcessingLayer" // ProcessingLayer - revision 21 + revision 22 modified_by 5 "hiv" // class settings //class diagram settings diff --git a/uml/lumiera/131973.diagram b/uml/lumiera/131973.diagram index 4b7003a43..dda416518 100644 --- a/uml/lumiera/131973.diagram +++ b/uml/lumiera/131973.diagram @@ -14,7 +14,7 @@ classcanvas 129669 class_ref 133253 // Frame end classcanvas 131845 class_ref 142469 // StateProxy draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 126 211 2000 + xyz 126 220 2000 end classcanvas 131973 class_ref 142597 // StateAdapter draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default @@ -22,11 +22,13 @@ classcanvas 131973 class_ref 142597 // StateAdapter end classcanvas 132357 class_ref 142725 // WiringDescriptor draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 229 497 2000 + hidden_operations + operation_ref 136453 // callDown + xyz 229 498 2000 end classcanvas 135045 class_ref 143365 // NodeWiring draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 241 568 2000 + xyz 241 561 2000 end textcanvas 135685 "determines concrete Type" xyzwh 100 594 2004 130 18 @@ -37,15 +39,15 @@ relationcanvas 129797 relation_ref 134533 // no_multiplicity_a multiplicity_b_pos 173 109 3000 relationcanvas 132101 relation_ref 148869 // geometry VHV unfixed - from ref 131973 z 1999 to point 78 181 - line 134021 z 1999 to point 118 181 + from ref 131973 z 1999 to point 78 186 + line 134021 z 1999 to point 118 186 line 134149 z 1999 to ref 128517 no_role_a no_role_b no_multiplicity_a no_multiplicity_b relationcanvas 132229 relation_ref 148997 // geometry VHV - from ref 131845 z 1999 to point 178 181 - line 134277 z 1999 to point 118 181 + from ref 131845 z 1999 to point 178 186 + line 134277 z 1999 to point 118 186 line 134405 z 1999 to ref 128517 no_role_a no_role_b no_multiplicity_a no_multiplicity_b @@ -53,13 +55,13 @@ relationcanvas 132485 relation_ref 149125 // geometry HV from ref 128005 z 1999 stereotype "<>" xyz 205 454 3000 to point 277 452 line 133381 z 1999 to ref 132357 - role_a_pos 289 472 3000 no_role_b - multiplicity_a_pos 265 472 3000 no_multiplicity_b + role_a_pos 289 473 3000 no_role_b + multiplicity_a_pos 265 473 3000 no_multiplicity_b relationcanvas 133509 relation_ref 149253 // from ref 131973 z 1999 to point 178 312 line 133637 z 1999 to ref 131845 - role_a_pos 190 302 3000 no_role_b - multiplicity_a_pos 164 302 3000 no_multiplicity_b + role_a_pos 190 293 3000 no_role_b + multiplicity_a_pos 164 293 3000 no_multiplicity_b relationcanvas 134533 relation_ref 149381 // from ref 131973 z 3004 to ref 129669 no_role_a no_role_b diff --git a/uml/lumiera/132357.diagram b/uml/lumiera/132357.diagram index a097c7137..5a7ffa76a 100644 --- a/uml/lumiera/132357.diagram +++ b/uml/lumiera/132357.diagram @@ -1,81 +1,172 @@ format 40 -classcanvas 128005 class_ref 132741 // State +classcanvas 128133 class_ref 143493 // OperationBase draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 78 10 2000 + xyz 232 13 2000 end -classcanvas 128133 class_ref 143493 // InvocationStateBase +classcanvas 128261 class_ref 143621 // QueryCache draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 203 104 2000 + xyz 186 252 2000 end -classcanvas 128261 class_ref 143621 // Caching +classcanvas 128517 class_ref 143877 // ReadSource draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 235 157 2000 + xyz 297 165 2000 end -classcanvas 128389 class_ref 143749 // Process +classcanvas 130309 class_ref 144005 // BuffTable draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 236 212 2000 - end -classcanvas 128517 class_ref 143877 // InPlace - draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 238 268 2000 - end -classcanvas 128645 class_ref 142469 // StateProxy - draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 209 332 2004 - end -classcanvas 130309 class_ref 144005 // WiringTable - draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 101 298 2000 + xyz 100 508 2000 end classcanvas 130437 class_ref 142725 // WiringDescriptor draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 36 213 2005 + xyz 82 319 2005 end classcanvas 130565 class_ref 144133 // BuffHandle draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default - xyz 36 379 2000 + xyz 169 563 2000 end textcanvas 131461 "const" - xyzwh 109 257 2010 29 18 -relationcanvas 128901 relation_ref 149765 // - from ref 128645 z 1999 to ref 128517 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -relationcanvas 129029 relation_ref 149893 // - from ref 128517 z 1999 to ref 128389 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -relationcanvas 129157 relation_ref 150021 // - from ref 128389 z 1999 to ref 128261 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -relationcanvas 129285 relation_ref 150149 // - from ref 128261 z 1999 to ref 128133 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b -relationcanvas 129413 relation_ref 150277 // - from ref 128133 z 1999 to point 260 69 - line 129925 z 1999 to point 190 69 - line 130181 z 1999 to point 161 176 - line 130053 z 1999 to point 118 176 - line 129797 z 1999 to ref 128005 - no_role_a no_role_b - no_multiplicity_a no_multiplicity_b + xyzwh 101 388 2010 29 18 +classcanvas 131717 class_ref 144261 // Invocation + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + color lightorange + xyz 31 439 2000 + end +classcanvas 131845 class_ref 143365 // NodeWiring + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 222 423 2005 + end +classcanvas 131973 class_ref 142597 // StateAdapter + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 25 230 2005 + end +classcanvas 132357 class_ref 144389 // ProcessData + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 186 140 2015 + end +classcanvas 132997 class_ref 144517 // Strategy + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 199 319 3010 + end +classcanvas 133253 class_ref 144517 // Strategy + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 301 301 2005 + end +classcanvas 134021 class_ref 144645 // FeedCache + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 190 80 2010 + end +classcanvas 134533 class_ref 144389 // ProcessData + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 238 125 2004 + end +classcanvas 134661 class_ref 144517 // Strategy + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 250 309 3005 + end +textcanvas 137477 "as configured" + xyzwh 308 376 3010 68 18 +classcanvas 137733 class_ref 143749 // PullInput + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 194 198 2010 + end +classcanvas 137861 class_ref 143749 // PullInput + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 247 184 2005 + end +textcanvas 139781 "Policy Classes" + xyzwh 163 50 2005 81 21 relationcanvas 130693 relation_ref 150405 // geometry VHr - from ref 130309 z 1999 stereotype "<>" xyz 43 309 3000 to point 70 326 + from ref 130309 z 1999 stereotype "<>" xyz 177 517 3000 to point 203 536 line 130821 z 1999 to ref 130565 no_role_a no_role_b - multiplicity_a_pos 45 354 3000 no_multiplicity_b + multiplicity_a_pos 211 537 3000 no_multiplicity_b relationcanvas 130949 relation_ref 150533 // from ref 130309 z 1999 to ref 130437 no_role_a no_role_b no_multiplicity_a no_multiplicity_b -relationcanvas 131077 relation_ref 150661 // - geometry VHr - from ref 128645 z 1999 to point 136 371 - line 131333 z 1999 to ref 130309 - role_a_pos 148 367 3000 no_role_b - multiplicity_a_pos 122 359 3000 no_multiplicity_b +relationcanvas 132229 relation_ref 149509 // + from ref 131845 z 2004 to point 219 401 + line 137605 z 2004 to point 162 401 + line 137221 z 2004 to ref 130437 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 133381 relation_ref 151301 // + from ref 133253 z 1999 to point 333 274 + line 137349 z 1999 to ref 128517 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 135685 relation_ref 152197 // + from ref 131717 z 1999 to ref 131973 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 135813 relation_ref 152325 // + from ref 131717 z 1999 to ref 130437 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 135941 relation_ref 152453 // + geometry VH + from ref 131717 z 1999 to point 63 536 + line 137093 z 1999 to ref 130309 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 136069 relation_ref 152581 // + from ref 131845 z 2004 to ref 132997 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 136197 relation_ref 152709 // + from ref 131845 z 2004 to ref 134661 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 136325 relation_ref 152837 // + from ref 131845 z 2004 to ref 133253 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 136453 relation_ref 152965 // + from ref 131845 z 1999 stereotype "<>" xyz 145 441 3000 to ref 131717 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 137989 relation_ref 153093 // + from ref 132997 z 1999 to point 222 316 + line 139653 z 1999 to ref 128261 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 138117 relation_ref 153221 // + from ref 128261 z 1999 to ref 137733 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 138245 relation_ref 153349 // + from ref 137733 z 2009 to ref 132357 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 138373 relation_ref 153477 // + from ref 132357 z 2009 to ref 134021 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 138501 relation_ref 153605 // + geometry VHV unfixed + from ref 134021 z 1999 to point 223 68 + line 138885 z 1999 to point 275 68 + line 139013 z 1999 to ref 128133 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 138629 relation_ref 153733 // + geometry VHV unfixed + from ref 128517 z 1999 to point 333 68 + line 139141 z 1999 to point 275 68 + line 139269 z 1999 to ref 128133 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 138757 relation_ref 153861 // + from ref 134533 z 1999 to ref 128133 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 139397 relation_ref 153989 // + from ref 134661 z 2004 to ref 137861 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 139525 relation_ref 154117 // + from ref 137861 z 2003 to ref 134533 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 1a789da17..e24c87ad6 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -1,22 +1,32 @@ -window_sizes 1140 830 270 860 680 71 +window_sizes 1302 1004 270 1022 854 71 diagrams classdiagram_ref 131973 // Render Mechanics - 428 623 100 4 2 0 + 407 690 100 4 2 0 collaborationdiagram_ref 132229 // Render Process - 817 644 100 4 0 0 + 825 684 100 4 0 0 active classdiagram_ref 132357 // StateAdapter composition - 418 520 100 4 0 0 + 414 658 100 4 0 0 end show_stereotypes selected -package_ref 129 // lumiera + package_ref 129 // lumiera open class_ref 132741 // State -package_ref 129029 // Controller + package_ref 129029 // Controller class_ref 131717 // ProcNode class_ref 142469 // StateProxy class_ref 142597 // StateAdapter + class_ref 142725 // WiringDescriptor + class_ref 143365 // NodeWiring + class_ref 143621 // QueryCache + class_ref 143749 // PullInput + class_ref 143877 // ReadSource + class_ref 144389 // ProcessData + class_ref 144645 // FeedCache + class_ref 144517 // Strategy + class_ref 144005 // BuffTable + class_ref 144261 // Invocation usecaseview_ref 128005 // Renderengine Use class_ref 140677 // QueryHandler class_ref 140805 // TypeHandler diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index 2238424c2..a533da6cc 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 40 "lumiera" - revision 47 + revision 48 modified_by 5 "hiv" cpp_root_dir "../../src/" diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 6384ef114..51956bb30 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -900,12 +900,12 @@ at the lowest level within the builder there is the step of building a //connect &rarr;see also: BuilderPrimitives for the elementary working situations corresponding to each of these [[builder moulds|BuilderMould]] -
    +
    Background: #fefefd
     Foreground: #000
     PrimaryPale: #8fb
    -PrimaryLight: #50d2ae
    -PrimaryMid: #1b9989
    +PrimaryLight: #4dc9a7
    +PrimaryMid: #16877a
     PrimaryDark: #0f3f56
     SecondaryPale: #ffc
     SecondaryLight: #fe8
    @@ -3043,7 +3043,7 @@ At first sight the link between asset and clip-MO is a simple logical relation b
     [img[Entities comprising the Render Engine|uml/fig128389.png]]
     
    -
    +
    Below are some notes regarding details of the actual implementation of the render process and processing node operation. In the description of the [[render node operation protocol|NodeOperationProtocol]] and the [[mechanics of the render process|RenderMechanics]], these details were left out deliberately.
     
     !Layered structure of State
    @@ -3059,18 +3059,19 @@ Some data processors simply require to work on multiple channels simultanously,
     [>img[uml/fig132357.png]]
     Every node is actually decomposed into three parts
     * an interface container of a ProcNode subclass
    -* an {{{const}}} WiringDescriptor, which is actually parametrized to a subtype encoding details of the intended operation
    -* an StateAdapter object created on the stack for each {{{pull()}}} call and configured specifically for the intended operation
    -Thus, the outer container can be changed polymorphically to support the different kinds of nodes (large-scale view). The actual wiring of the nodes is contained in the WiringDescriptor, including the {{{process()}}} function pointer. Additionally, this WiringDescriptor knows the actual type of the StateAdapter to create, and this actual type has been chosen by the builder such as to select details of the desired operation of this mode, for example caching / no caching or maybe ~OpenGL rendering or the special case of a node pulling directly from a source reader. Most of this configuration is done by selecting the right template specialisation within the builder; thus in the critical path most of the calls can be inlined
    +* an {{{const}}} WiringDescriptor, which is actually parametrized to a subtype encoding details of how to carry out the intended operation
    +* the Invocation state created on the stack for each {{{pull()}}} call. It is comprised of references to an StateAdapter object  and the current overall process state, the WiringDescriptor, and finally a table of suitable buffer handles
    +Thus, the outer container can be changed polymorphically to support the different kinds of nodes (large-scale view). The actual wiring of the nodes is contained in the WiringDescriptor, including the {{{process()}}} function pointer. Additionally, this WiringDescriptor knows the actual type of the operation Strategy, and this actual type has been chosen by the builder such as to select details of the desired operation of this node, for example caching / no caching or maybe ~OpenGL rendering or the special case of a node pulling directly from a source reader. Most of this configuration is done by selecting the right template specialisation within the builder; thus in the critical path most of the calls can be inlined
     
    -!!!! composing the actual StateAdapter
    -As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|StateAdapter]] for details). The rationale is that we can allocate at runtime a StateProxy object directly on the stack with the necessary buffer table size and an precisely tailored and mostly inlined implementation of the call sequence for "calculating the predecessor nodes". We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out the allocation of the "wiring table" into a hand-made stack-like buffer allocated from the heap.)
    +!!!! composing the actual operation Strategy
    +As shown in the class diagram to the right, the actual implementation is assembled by chaining together the various policy classes governing parts of the node operation, like Caching, in-Place calculation capability, etc. (&rarr; see [[here|WiringDescriptor]] for details). The rationale is that the variable part of the Invocation data is allocated at runtime directly on the stack, while a precisely tailored call sequence for "calculating the predecessor nodes" can be defined out of a bunch of simple building blocks. This helps avoiding "spaghetti code", which would be especially dangerous because of the large number of different execution paths to get right. Additionally, a nice side effect of this implementation technique is that a good deal of the implementation is eligible to inlining.
    +We //do employ//&nbsp; some virtual calls for the buffer management in order to avoid coupling the policy classes to the actual number of in/out buffers. (As of 6/2008, this is mainly a precaution to be able to control the number of generated template instances. If we ever get in the region of several hundred individual specialisations, we'd need to separate out the allocation of the "buffer table" into a hand-made stack-like buffer allocated from the heap.)
     
     !Rules for buffer allocation and freeing
     * only output buffers are allocated. It is //never necessary//&nbsp; to allocate input buffers!
     * buffers are to be allocated as late as possible, typically just before invoking {{{process()}}}
    -* buffers are allways allocated by calling to the preceeding StateAdapter in the callstack ("parent stae"), because of the possibility to write to cache.
    -* {{{pull()}}} returns the buffer-ID of the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StaeProxy.
    +* buffers are allways allocated by calling to the preceeding StateAdapter in the callstack ("parent stae"), because of the possibility of writing the result to cache.
    +* {{{pull()}}} returns a handle for the single output requested by this call. Using this ID, the caller may retrieve the actual buffer holding the result from the "current state" StaeProxy.
     * any other buffers filled with results in the course of the same {{{process()}}} call can be released immediately before returning from the {{{pull()}}}
     * similar, and input buffers are to be released immediately after the {{{process()}}} call, but before returing from this {{{pull()}}}
     * buffers are allways released by calling to the "current state" (which is a StateProxy), providing the buffer-ID to be released
    @@ -3246,32 +3247,8 @@ if (oldText.indexOf("SplashScreen")==-1)
     }
     //}}}
    -
    -
    A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. The actual type of this object is composed out of several building blocks (policy classes) and placed by the builder as a template parameter on the WiringDescriptor of the individual ProcNode. This happens in the WiringFactory in file {{{nodewiring.cpp}}}, which consequently contains all the possible combinations (pre)generated at compile time.
    -
    -!building blocks
    -* ''Caching'': whether the result frames of this processing step will be communicated to the Cache and thus could be fetched from there instead of actually calculating them.
    -* ''Process'': whether this node does any calculations on it's own or just pulls from a source
    -* ''Inplace'': whether this node is capable of processing the result "in-place", thereby overwriting the input buffer
    -* ''Multiout'': whether this node produces multiple output channels/frames in one processing step
    -
    -!!implementation
    -!!!!Caching
    -When a node participates in ''caching'', a result frame may be pulled immediately from cache instead of calculating it. Moreover, //any output buffer//&nbsp; of this node will be allocated //within the cache.// Consequently, caching interferes with the ability of the next node to calculate "in-Place". In the other case, when ''not using the cache'', the {{{pull()}}} call immediately starts out with calling down to the predecessor nodes, and the allocation of output buffer(s) is always delegated to the parent state (i.e. the StateProxy pulling results from this node). 
    -
    -Generally, buffer allocation requests from predecessor nodes (while being pulled by this node) will either be satisfied by using the "current state", or treated as if they were our own output buffers when this node is in-Place capable.
    -
    -!!!!Multiple Outputs
    -Some simplifications are possible in the default case of a node producing just ''one single output'' stream. Otherwise, we'd have to allocate multiple output buffers, and then, after processing, select the one needed as a result and deallocate the superfluous further buffers.
    -
    -!!!!in-Place capability
    -If a node is capable of calculating the result by ''modifying it's input'' buffer(s), an important performance optimization is possible, because in a chain of in-place capable nodes, we don't need any buffer allocations. But, on the other hand, this optimization may collide with the caching, because a frame retrieved from cache must not be modified.
    -Without this optimization, in the base case each processing needs an input and an output. Exceptionally, we could think of special nodes which //require// to process in-place, in which case we'd need to provide a copy of the input buffer to work on under certain circumstances. 
    -
    -!!!!Processing
    -If ''not processing'' we don't have any input buffers, instead we get our output buffers from an external source.
    -Otherwise, in the default case of actually ''processing'' out output, we have to organize input buffers, allocate output buffers, call the {{{process()}}} function of the WiringDescriptor and finally release the input buffers.
    -
    +
    +
    A small (in terms of storage) and specifically configured StateProxy object which is created on the stack for each individual {{{pull()}}} call. It is part of the invocation state of such a call and participates in the buffer management. Thus, in a calldown sequence of {{{pull()}}} calls we get a corresponding sequence of "parent" states. At each level, the &rarr; WiringDescriptor of the respective node defines a Strategy how the call is passed on.
    An Object representing a //Render Process// and containing associated state information.
    @@ -4648,6 +4625,33 @@ In case it's not already clear: we don't have "the" Render Engine, rat
     The &raquo;current setup&laquo; of the objects in the EDL is sort of a global state. Same holds true for the Controller, as the Engine can be at playback, it can run a background render or scrub single frames. But the whole complicated subsystem of the Builder and one given Render Engine configuration can be made ''stateless''. As a benefit of this we can run this subsystems multi-threaded without the need of any precautions (locking, synchronizing). Because all state information is just passed in as function parameters and lives in local variables on the stack, or is contained in the StateProxy which represents the given render //process// and is passed down as function parameter as well. (note: I use the term "stateless" in the usual, slightly relaxed manner; of course there are some configuration values contained in instance variables of the objects carrying out the calculations, but this values are considered to be constant over the course of the object usage).
     
    +
    +
    Each [[processing node|ProcNode]] contains a stateless ({{{const}}}) descriptor detailing the inputs, outputs and predecessors. Moreover, this descriptor contains the configuration of the call sequence yielding the &raquo;data pulled from predecessor(s)&laquo;. The actual type of this object is composed out of several building blocks (policy classes) and placed by the builder as a template parameter on the WiringDescriptor of the individual ProcNode. This happens in the WiringFactory in file {{{nodewiring.cpp}}}, which consequently contains all the possible combinations (pre)generated at compile time.
    +
    +!building blocks
    +* ''Caching'': whether the result frames of this processing step will be communicated to the Cache and thus could be fetched from there instead of actually calculating them.
    +* ''Process'': whether this node does any calculations on it's own or just pulls from a source
    +* ''Inplace'': whether this node is capable of processing the result "in-place", thereby overwriting the input buffer
    +* ''Multiout'': whether this node produces multiple output channels/frames in one processing step
    +
    +!!implementation
    +!!!!Caching
    +When a node participates in ''caching'', a result frame may be pulled immediately from cache instead of calculating it. Moreover, //any output buffer//&nbsp; of this node will be allocated //within the cache.// Consequently, caching interferes with the ability of the next node to calculate "in-Place". In the other case, when ''not using the cache'', the {{{pull()}}} call immediately starts out with calling down to the predecessor nodes, and the allocation of output buffer(s) is always delegated to the parent state (i.e. the StateProxy pulling results from this node). 
    +
    +Generally, buffer allocation requests from predecessor nodes (while being pulled by this node) will either be satisfied by using the "current state", or treated as if they were our own output buffers when this node is in-Place capable.
    +
    +!!!!Multiple Outputs
    +Some simplifications are possible in the default case of a node producing just ''one single output'' stream. Otherwise, we'd have to allocate multiple output buffers, and then, after processing, select the one needed as a result and deallocate the superfluous further buffers.
    +
    +!!!!in-Place capability
    +If a node is capable of calculating the result by ''modifying it's input'' buffer(s), an important performance optimization is possible, because in a chain of in-place capable nodes, we don't need any buffer allocations. But, on the other hand, this optimization may collide with the caching, because a frame retrieved from cache must not be modified.
    +Without this optimization, in the base case each processing needs an input and an output. Exceptionally, we could think of special nodes which //require// to process in-place, in which case we'd need to provide it with a copy of the input buffer to work on. 
    +
    +!!!!Processing
    +If ''not processing'' we don't have any input buffers, instead we get our output buffers from an external source.
    +Otherwise, in the default case of actually ''processing'' out output, we have to organize input buffers, allocate output buffers, call the {{{process()}}} function of the WiringDescriptor and finally release the input buffers.
    +
    +
    The purpose of automation is to vary a parameter of some data processing instance in the course of time while rendering. Thus, automation encompasses all the variability within the render network //which is not a structural change.//
     
    
    From 63d5684c12883242c68b65178ef2e8156ffe96ae Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Wed, 16 Jul 2008 05:09:04 +0200
    Subject: [PATCH 07/40] WIP continued with refactoring the Invocation
    
    ---
     src/common/frameid.hpp            |  56 +++++++++++++++
     src/proc/engine/buffhandle.cpp    |  32 +++++++++
     src/proc/engine/buffhandle.hpp    | 111 ++++++++++++++++++++++++++++++
     src/proc/engine/nodeoperation.hpp |  73 +++++++++++++-------
     src/proc/engine/nodewiring.hpp    |   7 ++
     src/proc/engine/procnode.hpp      |   3 +
     src/proc/state.hpp                |  55 ++++++++-------
     7 files changed, 290 insertions(+), 47 deletions(-)
     create mode 100644 src/common/frameid.hpp
     create mode 100644 src/proc/engine/buffhandle.cpp
     create mode 100644 src/proc/engine/buffhandle.hpp
    
    diff --git a/src/common/frameid.hpp b/src/common/frameid.hpp
    new file mode 100644
    index 000000000..9cb95eb37
    --- /dev/null
    +++ b/src/common/frameid.hpp
    @@ -0,0 +1,56 @@
    +/*
    +  FRAMEID.hpp  -  distinct identification of a single data frame
    + 
    +  Copyright (C)         Lumiera.org
    +    2008,               Hermann Vosseler 
    + 
    +  This program is free software; you can redistribute it and/or
    +  modify it under the terms of the GNU General Public License as
    +  published by the Free Software Foundation; either version 2 of the
    +  License, or (at your option) any later version.
    + 
    +  This program is distributed in the hope that it will be useful,
    +  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +  GNU General Public License for more details.
    + 
    +  You should have received a copy of the GNU General Public License
    +  along with this program; if not, write to the Free Software
    +  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + 
    +*/
    +
    +
    +#ifndef LUMIERA_FRAMEID_H
    +#define LUMIERA_FRAMEID_H
    +
    +
    +
    +namespace lumiera
    +  {
    +
    +
    +  /**
    +   * Identification tuple for addressing frames unambiguosly.
    +   * 
    +   * @todo currently (7/08) this is a dummy implementation to find out
    +   *       what interface the Proc layer needs. Probably the backend will
    +   *       later on define what is actually needed; this header should then
    +   *       be replaced by a combined C/C++ header
    +   */
    +  class FrameID : boost::totally_ordered
    +    {
    +      long dummy;
    +    public:
    +      FrameID(long dum=0) : dummy(dum) {}
    +      operator long () { return dummy; }
    +      
    +      bool operator<  (const FrameID& of)  const { return dummy <  of.dummy; }
    +      bool operator== (const FrameID& of)  const { return dummy == of.dummy; }
    +    };
    +    
    +  
    +
    +   
    +} // namespace lumiera
    +#endif
    diff --git a/src/proc/engine/buffhandle.cpp b/src/proc/engine/buffhandle.cpp
    new file mode 100644
    index 000000000..30ae603e2
    --- /dev/null
    +++ b/src/proc/engine/buffhandle.cpp
    @@ -0,0 +1,32 @@
    +/*
    +  BuffHandle  -  Buffer handling support for the render engine
    + 
    +  Copyright (C)         Lumiera.org
    +    2008,               Hermann Vosseler 
    + 
    +  This program is free software; you can redistribute it and/or
    +  modify it under the terms of the GNU General Public License as
    +  published by the Free Software Foundation; either version 2 of the
    +  License, or (at your option) any later version.
    + 
    +  This program is distributed in the hope that it will be useful,
    +  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +  GNU General Public License for more details.
    + 
    +  You should have received a copy of the GNU General Public License
    +  along with this program; if not, write to the Free Software
    +  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + 
    +* *****************************************************/
    +
    +
    +#include "proc/engine/buffhandle.hpp"
    +
    +namespace engine {
    +
    +
    +  /**  */
    +
    +
    +} // namespace engine
    diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
    new file mode 100644
    index 000000000..d72ff7193
    --- /dev/null
    +++ b/src/proc/engine/buffhandle.hpp
    @@ -0,0 +1,111 @@
    +/*
    +  BUFFHANDLE.hpp  -  Buffer handling support for the render engine
    + 
    +  Copyright (C)         Lumiera.org
    +    2008,               Hermann Vosseler 
    + 
    +  This program is free software; you can redistribute it and/or
    +  modify it under the terms of the GNU General Public License as
    +  published by the Free Software Foundation; either version 2 of the
    +  License, or (at your option) any later version.
    + 
    +  This program is distributed in the hope that it will be useful,
    +  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +  GNU General Public License for more details.
    + 
    +  You should have received a copy of the GNU General Public License
    +  along with this program; if not, write to the Free Software
    +  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + 
    +*/
    +
    +/** @file procnode.hpp
    + ** Interface to the processing nodes and the render nodes network.
    + **
    + ** Actually, there are three different interfaces to consider
    + ** - the ProcNode#pull is the invocation interface. It is call-style (easily callable by C)
    + ** - the builder interface, comprised by the NodeFactory and the WiringFactory. It's C++ (using templates)
    + ** - the actual processing function is supposed to be a C function; it uses a set of C functions 
    + **   for accessing the frame buffers with the data to be processed.
    + **
    + ** By using the builder interface, concrete node and wiring descriptor classes are created,
    + ** based on some templates. These concrete classes form the "glue" to tie the node network
    + ** together and contain much of the operation beahviour in a hard wired fashion.
    + **
    + ** @see nodefactory.hpp
    + ** @see operationpoint.hpp
    + */
    +
    +#ifndef ENGINE_BUFFHANDLE_H
    +#define ENGINE_BUFFHANDLE_H
    +
    +
    +//#include "proc/mobject/parameter.hpp"
    +
    +//#include 
    +
    +
    +
    +namespace engine {
    +
    +//  using std::vector;
    +  
    +  
    +  /**
    +   * Handle for a buffer for processing data, abstracting away the actual implementation.
    +   * The real buffer pointer can be retrieved by dereferencing this smart-handle class.
    +   */
    +  struct BuffHandle
    +    {
    +      typedef float Buff;
    +      typedef Buff* PBuff;//////TODO define the Buffer type(s)
    +      
    +      PBuff 
    +      operator->() const 
    +        { 
    +          return pBuffer_; 
    +        }
    +      Buff&
    +      operator* () const
    +        {
    +          ENSURE (pBuffer_);
    +          return *pBuffer_;
    +        }
    +      
    +    protected:
    +      PBuff pBuffer_; 
    +      long sourceID_;
    +    };
    +  
    +  
    +  /**
    +   * Buffer Type information.
    +   * Given a BufferDescriptor, it is possible to allocate a buffer
    +   * of suitable size and type by using State::allocateBuffer().
    +   */
    +  struct BufferDescriptor
    +    {
    +      char typeID_; ///////TODO define the Buffer type(s)
    +    };
    +  
    +  
    +    /** 
    +     * Tables of buffer handles and corresponding dereferenced buffer pointers.
    +     * Used within the invocation of a processing node to calculate data.
    +     * The tables are further differentiated into input data buffers and output
    +     * data buffers. The tables are supposed to be implemented as bare "C" arrays,
    +     * thus the array of real buffer pointers can be fed directly to any processing
    +     * function
    +     */
    +  struct BuffTable
    +    {
    +      BuffHandle        *const outHandle;
    +      BuffHandle        *const inHandle;
    +      BuffHandle::PBuff *const outBuff;
    +      BuffHandle::PBuff *const inBuff;
    +    };
    +  
    +  
    +} // namespace engine
    +#endif
    diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
    index c8740def0..86a41e896 100644
    --- a/src/proc/engine/nodeoperation.hpp
    +++ b/src/proc/engine/nodeoperation.hpp
    @@ -52,7 +52,9 @@
     #define ENGINE_NODEOPERATION_H
     
     
    +#include "proc/state.hpp"
     #include "proc/engine/procnode.hpp"
    +#include "proc/engine/buffhandle.hpp"
     #include "proc/engine/nodewiringconfig.hpp"
     #include "lib/appconfig.hpp"
     
    @@ -88,8 +90,6 @@ namespace engine {
           State& parent_;
           State& current_;
           
    -      uint requiredOutputNr;
    -
           StateAdapter (State& callingProcess) 
             : parent_ (callingProcess),
               current_(callingProcess.getCurrentImplementation())
    @@ -97,44 +97,71 @@ namespace engine {
           
           virtual State& getCurrentImplementation () { return current_; }
           
    +      
    +    public: /* === proxying the State interface === */
    +      
    +      virtual void releaseBuffer (BuffHandle& bh)       { current_.releaseBuffer (bh); }
    +      
    +      virtual void is_calculated (BuffHandle const& bh) { current_.is_calculated (bh); }
    +      
    +      virtual BuffHandle fetch (FrameID const& fID)     { return current_.fetch (fID); }
    +      
    +      
    +      // note: allocateBuffer()  is choosen specifically based on the actual node wiring
    +      
         };
       
    -  struct BuffTable
    -    {
    -      BuffHandle        *const outHandle;
    -      BuffHandle        *const inHandle;
    -      BuffHandle::PBuff *const outBuff;
    -      BuffHandle::PBuff *const inBuff;
    -    };
    -  
    +    
       
       struct Invocation
    +    : StateAdapter
         {
           WiringDescriptor const& wiring;
           BuffTable& buffTab;
           const uint outNr;
           
         protected:
    -      Invocation (WiringDescriptor const& w, BuffTable& b, uint o)
    -        : wiring(w),
    +      Invocation (State& callingProcess, WiringDescriptor const& w, BuffTable& b, uint o)
    +        : StateAdapter(callingProcess),
    +          wiring(w),
               buffTab(b),
               outNr(o)
             { }
         };
    -  
    -  
    +
       template
    +  class BuffTableStorage
    +    : public BuffTable
    +    {
    +      BuffHandle hTab_[SZ];
    +      BuffHandle::PBuff pTab_[SZ];
    +      
    +    protected:
    +      BuffTableStorage (WiringDescriptor const& wd)
    +        {
    +          const uint nrI = wd.getNrI();
    +          const uint nrO = wd.getNrO();
    +          REQUIRE (nrI+nrO <= SZ);
    +          
    +          this->outHandle = &hTab_[0];
    +          this->inHandle  = &hTab_[nrO];
    +          this->outBuff   = &pTab_[0];
    +          this->inBuff    = &pTab_[nrO];
    +        }
    +    };
    +  
    +  
    +  
    +  template /////////////////TODO: rather template on the BufferProvider
       class InvocationImpl
    -    : private StateAdapter,
    -      private BuffTableStorage,
    +    : private BuffTableStorage,
           public Invocation
         {
     
         protected:
           InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh)
    -        : StateAdapter(callingProcess),
    -          BuffTableStorage(w),
    -          Invocation(w, static_cast(*this), outCh)
    +        : BuffTableStorage(w),
    +          Invocation(callingProcess, w, static_cast(*this), outCh)
             { }
           
           /** contains the details of Cache query and recursive calls
    @@ -266,13 +293,13 @@ namespace engine {
       template<>
       struct OutBuffSource
         {
    -      State& getBufferProvider (InvocationStateBase& thisState) { return thisState.parent_; }
    +      static State& getBufferProvider (Invocation& thisState) { return thisState.parent_; }
         };
       
       template<>
       struct OutBuffSource
         {
    -      State& getBufferProvider (InvocationStateBase& thisState) { return thisState.current_; }
    +      static State& getBufferProvider (Invocation& thisState) { return thisState.current_; }
         };
       
       
    @@ -331,7 +358,7 @@ namespace engine {
       struct Strategy ;
     
       
    -  template
    +  template
       struct SelectBuffProvider;
       
       template<> struct SelectBuffProvider         : OutBuffSource { };
    @@ -354,7 +381,7 @@ namespace engine {
         { };
       
       template
    -  struct Strategy< Config >
    +  struct Strategy< Config >
         : PullInput<
            AllocOutput,
             ProcessData<
    diff --git a/src/proc/engine/nodewiring.hpp b/src/proc/engine/nodewiring.hpp
    index 0f6cc9500..d17b95396 100644
    --- a/src/proc/engine/nodewiring.hpp
    +++ b/src/proc/engine/nodewiring.hpp
    @@ -49,9 +49,16 @@ namespace engine {
       class NodeWiring
         : public WiringDescriptor
         {
    +      const uint siz_;
    +      const uint nrO_; 
           
           friend class WiringFactory;
           
    +    public:
    +      virtual uint getNrI()  const { return siz_ - nrO_; }
    +      virtual uint getNrO()  const { return nrO_; }
    +
    +      
         protected:
           virtual BuffHandle
           callDown (State& currentProcess, uint requiredOutputNr)  const 
    diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp
    index 4a3249dc9..2d5af1a3a 100644
    --- a/src/proc/engine/procnode.hpp
    +++ b/src/proc/engine/procnode.hpp
    @@ -70,6 +70,9 @@ namespace engine {
         public:
           virtual ~WiringDescriptor() {}
           
    +      virtual uint getNrI()  const =0;           ///////////TODO: indeed need a virtual function??
    +      virtual uint getNrO()  const =0;
    +      
         protected:
           /** the wiring-dependent part of the node operation.
            *  Includes the creation of a one-way state object on the stack
    diff --git a/src/proc/state.hpp b/src/proc/state.hpp
    index 2f047ee7a..cc8bf69d9 100644
    --- a/src/proc/state.hpp
    +++ b/src/proc/state.hpp
    @@ -28,35 +28,17 @@
     #include "pre.hpp"
     
     #include "proc/lumiera.hpp"
    +#include "common/frameid.hpp"
    +#include "proc/engine/buffhandle.hpp"
     
     #include 
     
     
     namespace engine { 
       
    -  class InvocationStateBase;
    -  
    -  struct BuffHandle
    -    {
    -      typedef float Buff;
    -      typedef Buff* PBuff;//////TODO define the Buffer type(s)
    -      
    -      PBuff 
    -      operator->() const 
    -        { 
    -          return pBuffer_; 
    -        }
    -      Buff&
    -      operator* () const
    -        {
    -          ENSURE (pBuffer_);
    -          return *pBuffer_;
    -        }
    -      
    -    protected:
    -      PBuff pBuffer_; 
    -      long sourceID_;
    -    };
    +  using lumiera::FrameID;
    +
    +  class StateAdapter;
       
       
       class State
    @@ -72,9 +54,34 @@ namespace engine {
            */ 
           virtual State& getCurrentImplementation () =0;
           
    -      friend class engine::InvocationStateBase;
    +      friend class engine::StateAdapter;
           
         public:
    +      /** allocate a new writable buffer with type and size according to
    +       *  the BufferDescriptor. The actual provider of this buffer depends
    +       *  on the State implementation; it could be a temporary, located in
    +       *  the cache, used for feeding calculated frames over a network, etc.
    +       *  @return a BuffHandle encapsulating the information necessary to get
    +       *          at the actual buffer adress and for releasing the buffer.
    +       */
    +      virtual BuffHandle allocateBuffer (BufferDescriptor const&)  =0;
    +      
    +      /** resign control of the buffer denoted by the handle */
    +      virtual void releaseBuffer (BuffHandle&)  =0;
    +      
    +      /** declare the data contained in the Buffer to be ready.
    +       *  The caller is required to restrain itself from modifying the data
    +       *  afterwards, as this buffer now can be used (readonly) by other
    +       *  calculation processes in parallel.
    +       */
    +      virtual void is_calculated (BuffHandle const&)  =0;
    +      
    +      /** try to fetch an existing buffer containing the denoted frame from
    +       *  a cache or similar backing system (e.g. peer over the network).
    +       *  @return either a handle to a readonly buffer, or a null handle
    +       *  @note the client is resposible for not modifying the provided data
    +       */
    +      virtual BuffHandle fetch (FrameID const&)  =0;
           
         };
       
    
    From 48780bef74f3a857cc9483f535fcd9c711bb9187 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 17 Jul 2008 16:17:43 +0200
    Subject: [PATCH 08/40] WIP draft the handling of buffer pointer tables
    
    ---
     src/proc/engine/buffhandle.hpp    |  50 +++------
     src/proc/engine/bufftable.hpp     | 162 ++++++++++++++++++++++++++++++
     src/proc/engine/nodeoperation.hpp |  21 +---
     src/proc/state.hpp                |   4 +
     4 files changed, 181 insertions(+), 56 deletions(-)
     create mode 100644 src/proc/engine/bufftable.hpp
    
    diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
    index d72ff7193..60ca7758d 100644
    --- a/src/proc/engine/buffhandle.hpp
    +++ b/src/proc/engine/buffhandle.hpp
    @@ -20,36 +20,30 @@
      
     */
     
    -/** @file procnode.hpp
    - ** Interface to the processing nodes and the render nodes network.
    +/** @file buffhandle.hpp
    + ** Various bits needed to support the buffer management within the render nodes.
    + ** When pulling data from predecessor nodes and calculating new data, each render node
    + ** needs several input and output buffers. These may be allocated and provided by several
    + ** different "buffer providers" (for example the frame cache). For accessing those buffers,
    + ** the node needs to keep a table of buffer pointers, and for releasing the buffers later
    + ** on, we need some handles. The usage pattern of those buffer pointer tables is stack-like,
    + ** thus it makes sense to utilize a single large buffer pointer array per pull() calldown
    + ** sequence and dynamically claim small chunks for each node.
      **
    - ** Actually, there are three different interfaces to consider
    - ** - the ProcNode#pull is the invocation interface. It is call-style (easily callable by C)
    - ** - the builder interface, comprised by the NodeFactory and the WiringFactory. It's C++ (using templates)
    - ** - the actual processing function is supposed to be a C function; it uses a set of C functions 
    - **   for accessing the frame buffers with the data to be processed.
    - **
    - ** By using the builder interface, concrete node and wiring descriptor classes are created,
    - ** based on some templates. These concrete classes form the "glue" to tie the node network
    - ** together and contain much of the operation beahviour in a hard wired fashion.
    - **
    - ** @see nodefactory.hpp
    - ** @see operationpoint.hpp
    + ** @see nodeoperation.hpp
    + ** @see bufftable.hpp       storage for the buffer table
    + ** @see engine::Invocation
      */
     
     #ifndef ENGINE_BUFFHANDLE_H
     #define ENGINE_BUFFHANDLE_H
     
     
    -//#include "proc/mobject/parameter.hpp"
    -
    -//#include 
    -
    +#include "common/error.hpp"
     
     
     namespace engine {
    -
    -//  using std::vector;
    +  
       
       
       /**
    @@ -90,22 +84,6 @@ namespace engine {
         };
       
       
    -    /** 
    -     * Tables of buffer handles and corresponding dereferenced buffer pointers.
    -     * Used within the invocation of a processing node to calculate data.
    -     * The tables are further differentiated into input data buffers and output
    -     * data buffers. The tables are supposed to be implemented as bare "C" arrays,
    -     * thus the array of real buffer pointers can be fed directly to any processing
    -     * function
    -     */
    -  struct BuffTable
    -    {
    -      BuffHandle        *const outHandle;
    -      BuffHandle        *const inHandle;
    -      BuffHandle::PBuff *const outBuff;
    -      BuffHandle::PBuff *const inBuff;
    -    };
    -  
       
     } // namespace engine
     #endif
    diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp
    new file mode 100644
    index 000000000..21ada3c88
    --- /dev/null
    +++ b/src/proc/engine/bufftable.hpp
    @@ -0,0 +1,162 @@
    +/*
    +  BUFFTABLE.hpp  -  Table of buffer pointers to be used by the render nodes
    + 
    +  Copyright (C)         Lumiera.org
    +    2008,               Hermann Vosseler 
    + 
    +  This program is free software; you can redistribute it and/or
    +  modify it under the terms of the GNU General Public License as
    +  published by the Free Software Foundation; either version 2 of the
    +  License, or (at your option) any later version.
    + 
    +  This program is distributed in the hope that it will be useful,
    +  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +  GNU General Public License for more details.
    + 
    +  You should have received a copy of the GNU General Public License
    +  along with this program; if not, write to the Free Software
    +  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + 
    +*/
    +
    +
    +#ifndef ENGINE_BUFFHTABLE_H
    +#define ENGINE_BUFFHTABLE_H
    +
    +
    +#include "common/error.hpp"
    +#include "proc/engine/buffhandle.hpp"
    +
    +#include 
    +#include 
    +#include 
    +
    +
    +
    +namespace engine {
    +  
    +  using std::pair;
    +  using std::vector;
    +  
    +  
    +    /** 
    +     * Tables of buffer handles and corresponding dereferenced buffer pointers.
    +     * Used within the invocation of a processing node to calculate data.
    +     * The tables are further differentiated into input data buffers and output
    +     * data buffers. The tables are supposed to be implemented as bare "C" arrays,
    +     * thus the array of real buffer pointers can be fed directly to the
    +     * processing function of the respective node.
    +     */
    +  struct BuffTable
    +    {
    +      typedef BuffHandle        * PHa;
    +      typedef BuffHandle::PBuff * PBu;
    +      typedef pair Chunk;
    +      
    +      PHa outHandle;
    +      PHa inHandle;
    +      PBu outBuff;
    +      PBu inBuff;
    +    };
    +  
    +  
    +  class BuffTableStorage;
    +  class WiringDescriptor;
    +  
    +  /** 
    +   * to be allocated on the stack while evaluating a ProcNode#pull() call.
    +   * The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
    +   * which can be used to crate such chunks. The claiming and releasing of
    +   * slots in the BuffTableStorage is automatically tied to BuffTableChunk
    +   * object's lifecycle.
    +   */
    +  class BuffTableChunk
    +    : public BuffTable,
    +      boost::noncopyable
    +    {
    +      const uint siz_;
    +      BuffTable::Chunk tab_;
    +      BuffTableStorage& sto_;
    +      
    +    public:
    +      BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
    +        : siz_(wd.getNrI() + wd.getNrO()),
    +          tab_(storage.claim (siz_)),
    +          sto_(storage)
    +        {
    +          const uint nrO(wd.getNrO());
    +          
    +          // Setup the public visible table locations
    +          this->outHandle = &tab_.first[ 0 ];
    +          this->inHandle  = &tab_.first[nrO];
    +          this->outBuff   = &tab_.second[ 0 ];
    +          this->inBuff    = &tab_.second[nrO];
    +        }
    +      
    +      ~BuffTableChunk ()
    +        {
    +          sto_.release (siz_);
    +          ASSERT ( sto_.level_check (tab_),
    +                  "buffer management logic broken.");
    +        }
    +    };
    +  
    +  
    +  
    +  class BuffTableStorage
    +    {
    +      vector        hTab_;
    +      vector pTab_;
    +      size_t level_;
    +      
    +    public:
    +      BuffTableStorage (const size_t maxSiz)
    +        : hTab_(maxSiz),
    +          pTab_(maxSiz),
    +          level_(0)
    +        { }
    +      
    +      ~BuffTableStorage() { ASSERT (0==level_, "buffer management logic broken."); }
    +      
    +    protected:
    +      
    +      friend class BuffTableChunk;
    +      
    +      /** allocate the given number of slots
    +       *  starting at current level to be used
    +       *  by the newly created BuffTableChunk
    +       */
    +      BuffTable::Chunk
    +      claim (uint slots)
    +        {
    +          ASSERT (pTab_.size() == hTab_.size());
    +          REQUIRE (level_+slots <= hTab_.size());
    +          
    +          size_t prev_level (level_);
    +          level_ += slots;
    +          return std::make_pair (&hTab_[prev_level],
    +                                 &pTab_[prev_level]);
    +        }
    +      
    +      void
    +      release (uint slots)
    +        {
    +          ASSERT (slots <= level_);
    +          REQUIRE (level_ <= hTab_.size());
    +          REQUIRE (level_ <= pTab_.size());
    +          
    +          level_ -= slots;
    +        }
    +      
    +      bool
    +      level_check (BuffTable::Chunk& prev_level)
    +        {
    +          return prev_level.first  == &hTab_[level_]
    +              && prev_level.second == &pTab_[level_];
    +        }
    +    };
    +  
    +  
    +} // namespace engine
    +#endif
    diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
    index 86a41e896..f99dedbcb 100644
    --- a/src/proc/engine/nodeoperation.hpp
    +++ b/src/proc/engine/nodeoperation.hpp
    @@ -55,6 +55,7 @@
     #include "proc/state.hpp"
     #include "proc/engine/procnode.hpp"
     #include "proc/engine/buffhandle.hpp"
    +#include "proc/engine/bufftable.hpp"
     #include "proc/engine/nodewiringconfig.hpp"
     #include "lib/appconfig.hpp"
     
    @@ -129,26 +130,6 @@ namespace engine {
             { }
         };
     
    -  template
    -  class BuffTableStorage
    -    : public BuffTable
    -    {
    -      BuffHandle hTab_[SZ];
    -      BuffHandle::PBuff pTab_[SZ];
    -      
    -    protected:
    -      BuffTableStorage (WiringDescriptor const& wd)
    -        {
    -          const uint nrI = wd.getNrI();
    -          const uint nrO = wd.getNrO();
    -          REQUIRE (nrI+nrO <= SZ);
    -          
    -          this->outHandle = &hTab_[0];
    -          this->inHandle  = &hTab_[nrO];
    -          this->outBuff   = &pTab_[0];
    -          this->inBuff    = &pTab_[nrO];
    -        }
    -    };
       
       
       
    diff --git a/src/proc/state.hpp b/src/proc/state.hpp
    index cc8bf69d9..84732029d 100644
    --- a/src/proc/state.hpp
    +++ b/src/proc/state.hpp
    @@ -30,6 +30,7 @@
     #include "proc/lumiera.hpp"
     #include "common/frameid.hpp"
     #include "proc/engine/buffhandle.hpp"
    +#include "proc/engine/bufftable.hpp"
     
     #include 
     
    @@ -83,6 +84,9 @@ namespace engine {
            */
           virtual BuffHandle fetch (FrameID const&)  =0;
           
    +      /** necessary for creating a local BuffTableChunk */
    +      virtual BuffTableStorage& getBuffTableStorage()  =0; 
    +      
         };
       
     } // namespace engine
    
    From cb8ac94dcff6efa7e707c39818cd138691f67659 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Fri, 18 Jul 2008 21:21:46 +0200
    Subject: [PATCH 09/40] WIP finally got the design straight?
    
    ---
     src/proc/engine/bufftable.hpp     |  86 +++++-----
     src/proc/engine/nodeoperation.hpp | 262 +++++++++++++++++-------------
     src/proc/state.hpp                |   3 +-
     3 files changed, 195 insertions(+), 156 deletions(-)
    
    diff --git a/src/proc/engine/bufftable.hpp b/src/proc/engine/bufftable.hpp
    index 21ada3c88..ac1329265 100644
    --- a/src/proc/engine/bufftable.hpp
    +++ b/src/proc/engine/bufftable.hpp
    @@ -27,6 +27,7 @@
     
     #include "common/error.hpp"
     #include "proc/engine/buffhandle.hpp"
    +#include "proc/engine/procnode.hpp"
     
     #include 
     #include 
    @@ -61,49 +62,6 @@ namespace engine {
         };
       
       
    -  class BuffTableStorage;
    -  class WiringDescriptor;
    -  
    -  /** 
    -   * to be allocated on the stack while evaluating a ProcNode#pull() call.
    -   * The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
    -   * which can be used to crate such chunks. The claiming and releasing of
    -   * slots in the BuffTableStorage is automatically tied to BuffTableChunk
    -   * object's lifecycle.
    -   */
    -  class BuffTableChunk
    -    : public BuffTable,
    -      boost::noncopyable
    -    {
    -      const uint siz_;
    -      BuffTable::Chunk tab_;
    -      BuffTableStorage& sto_;
    -      
    -    public:
    -      BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
    -        : siz_(wd.getNrI() + wd.getNrO()),
    -          tab_(storage.claim (siz_)),
    -          sto_(storage)
    -        {
    -          const uint nrO(wd.getNrO());
    -          
    -          // Setup the public visible table locations
    -          this->outHandle = &tab_.first[ 0 ];
    -          this->inHandle  = &tab_.first[nrO];
    -          this->outBuff   = &tab_.second[ 0 ];
    -          this->inBuff    = &tab_.second[nrO];
    -        }
    -      
    -      ~BuffTableChunk ()
    -        {
    -          sto_.release (siz_);
    -          ASSERT ( sto_.level_check (tab_),
    -                  "buffer management logic broken.");
    -        }
    -    };
    -  
    -  
    -  
       class BuffTableStorage
         {
           vector        hTab_;
    @@ -158,5 +116,47 @@ namespace engine {
         };
       
       
    +  /** 
    +   * to be allocated on the stack while evaluating a ProcNode#pull() call.
    +   * The "current" State (StateProxy) maintains a BuffTableStorage (=pool),
    +   * which can be used to crate such chunks. The claiming and releasing of
    +   * slots in the BuffTableStorage is automatically tied to BuffTableChunk
    +   * object's lifecycle.
    +   */
    +  class BuffTableChunk
    +    : public BuffTable,
    +      boost::noncopyable
    +    {
    +      const uint siz_;
    +      BuffTable::Chunk tab_;
    +      BuffTableStorage& sto_;
    +      
    +    public:
    +      BuffTableChunk (WiringDescriptor const& wd, BuffTableStorage& storage)
    +        : siz_(wd.getNrI() + wd.getNrO()),
    +          tab_(storage.claim (siz_)),
    +          sto_(storage)
    +        {
    +          const uint nrO(wd.getNrO());
    +          
    +          // Setup the public visible table locations
    +          this->outHandle = &tab_.first[ 0 ];
    +          this->inHandle  = &tab_.first[nrO];
    +          this->outBuff   = &tab_.second[ 0 ];
    +          this->inBuff    = &tab_.second[nrO];
    +        }
    +      
    +      ~BuffTableChunk ()
    +        {
    +          sto_.release (siz_);
    +          ASSERT ( sto_.level_check (tab_),
    +                  "buffer management logic broken.");
    +        }
    +    };
    +  
    +  
    +  
    +  
    +  
     } // namespace engine
     #endif
    diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
    index f99dedbcb..6c0092edd 100644
    --- a/src/proc/engine/nodeoperation.hpp
    +++ b/src/proc/engine/nodeoperation.hpp
    @@ -73,16 +73,6 @@ namespace engine {
        * call, using setup/wiring data preconfigured by the builder.
        * Its job is to provide the actual implementation of the Cache
        * push / fetch and recursive downcall to render the source frames.
    -   * 
    -   * \par assembling the implementation
    -   * This is the abstract base class of all concrete StateAdapter types.
    -   * Each ProcNode#pull() call creates such a StateAdapter on the stack,
    -   * with a concrete type according to the WiringDescriptor of the node
    -   * to pull. The concrete type is assembled by a chain of policy templates
    -   * on top of InvocationStateBase. For each of the possible configuratons
    -   * we define such a chain (see bottom of this header). The WiringFactory
    -   * defined in nodewiring.cpp actually drives the instantiation of all
    -   * those possible combinations
        */
       class StateAdapter
         : public State
    @@ -97,6 +87,8 @@ namespace engine {
             { }
           
           virtual State& getCurrentImplementation () { return current_; }
    +
    +      typedef State& (StateAdapter::*BufferProvider);
           
           
         public: /* === proxying the State interface === */
    @@ -110,10 +102,28 @@ namespace engine {
           
           // note: allocateBuffer()  is choosen specifically based on the actual node wiring
           
    +      
    +    protected: /* === API for use by the node operation pull() call === */
    +      
    +      BufferDescriptor
    +      getBufferDescriptor (uint chan)
    +        {
    +          TODO ("determine BufferDescriptor based on WiringDescriptor channel info");
    +        }
         };
       
         
    -  
    +  /**
    +   * Invocation context state.
    +   * A ref to this type is carried through the chain of NEXT::step() functions
    +   * which form the actual invocation sequence. The various operations in this sequence
    +   * access the context via the references in this struct, and also using the inherited
    +   * public State intercace. The object instance actually used as Invocation is created
    +   * on the stack and parametrized according to the necessities of the invocation
    +   * sequence actually configured. This real object instance comes in two flavours:
    +   * - a intended/partial invocation (which may be short-circuited due to Cache hit)
    +   * - a complete invocation, including storage for managing the buffer handles.
    +   */
       struct Invocation
         : StateAdapter
         {
    @@ -122,43 +132,98 @@ namespace engine {
           const uint outNr;
           
         protected:
    -      Invocation (State& callingProcess, WiringDescriptor const& w, BuffTable& b, uint o)
    +      /** creates a new invocation context state, 
    +       *  configuring it as intended/partial invocation without BuffTable */
    +      Invocation (State& callingProcess, WiringDescriptor const& w, BufferProvider buffSrc, uint o)
             : StateAdapter(callingProcess),
    -          wiring(w),
    -          buffTab(b),
    -          outNr(o)
    +          wiring(w), outNr(o),
    +          bufferProvider_(buffSrc)
             { }
    +      
    +      /** used to build a complete invocation, including a buffer table,
    +          based on an existing intended/partial invocation */
    +      Invocation (Invocation const& currInvocation, BuffTable& localBuffTab)
    +        : StateAdapter(currInvocation),
    +          wiring(currInvocation.wiring),
    +          buffTab(localBuffTab),
    +          outNr(currInvocation.outNr),
    +          bufferProvider_(currInvocation.bufferProvider_)
    +        { }
    +      
    +      /** configuration of the actual entity in charge of allocating Buffers.
    +       *  depending on the configured call sequence, this may be a parent StateAdapter,
    +       *  or the backend in case we want to fade the results into the Cache  */
    +      const BufferProvider bufferProvider_;
    +      
    +    public:
    +      BuffHandle const&
    +      allocateBuffer (uint outChannel)
    +        {
    +          return this->*bufferProvider_.allocateBuffer(
    +                   this->getBufferDescriptor(outChannel));
    +        }
         };
     
       
       
    -  
    -  template /////////////////TODO: rather template on the BufferProvider
    -  class InvocationImpl
    -    : private BuffTableStorage,
    -      public Invocation
    +  /**
    +   * The real invocation context state implementation. It is created
    +   * by the NodeWiring (WiringDescriptor) of the processing node which
    +   * is pulled by this invocation, hereby using the internal configuration
    +   * information to guide the selecton of the real call sequence 
    +   * 
    +   * \par assembling the call sequence implementation
    +   * Each ProcNode#pull() call creates such a StateAdapter subclass on the stack,
    +   * with a concrete type according to the WiringDescriptor of the node to pull.
    +   * This concrete type encodes a calculation Strategy, which is assembled
    +   * as a chain of policy templates on top of OperationBase. For each of the
    +   * possible configuratons we define such a chain (see bottom of this header).
    +   * The WiringFactory defined in nodewiring.cpp actually drives the instantiation
    +   * of all those possible combinations.
    +   */
    +  template
    +  class ActualInvocationOrder
    +    : public Invocation
         {
    -
    -    protected:
    +    public:
           InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh)
    -        : BuffTableStorage(w),
    -          Invocation(callingProcess, w, static_cast(*this), outCh)
    +        : Invocation(callingProcess, w, buffPro, outCh)
             { }
           
           /** contains the details of Cache query and recursive calls
            *  to the predecessor node(s), eventually followed by the 
            *  ProcNode::process() callback
            */
    -      template
           BuffHandle retrieve ()
             {
               return Strategy::step (*this);
             }
         };
     
    +  
    +  /** 
    +   * Extends the invocation context state by a table of buffer pointers.
    +   * This extension is used whenever the call sequence goes into actually
    +   * invoking a process() function, which necessiates to build a array of
    +   * buffer pointers and to allocate buffers to hold the data to be worked on.
    +   * It is implemented as decorating an already created "partial invocation",
    +   * just adding the allocation of a Table Chunk for organizing the buffers.
    +   */
    +  class ProcessInvocation
    +    : protected BuffTableChunk,
    +      public Invocation
    +    {
    +    public:
    +      ProcessInvocation (Invocation const& currInvocation)
    +        : BuffTableChunk(currInvocation.wiring, currInvocation.getBuffTableStorage()),
    +          Invocation(currInvocation, static_cast(*this))
    +        { }
    +    };
         
         
    -    
    +  /**
    +   * Collection of functions used to build up the invocation sequence.
    +   */
       class OperationBase
         {
           
    @@ -170,9 +235,8 @@ namespace engine {
           BuffHandle
           step (Invocation& ivo)
             {
    -          BuffHandle fetched = this->current_.fetch (
    -                                 this->genFrameID (
    -                                   this->requiredOutputNr));
    +          BuffHandle fetched = ivo.fetch (
    +                                 this->genFrameID (ivo));
               if (fetched)
                 return fetched;
               else
    @@ -185,25 +249,25 @@ namespace engine {
       struct PullInput : NEXT
         {
           BuffHandle
    -      step (Invocation& ivo)
    +      step (Invocation& started_invocation)
             {
    -          this->createBuffTable();
    +          ProcessInvocation ivo (started_invocation);
               
    -          ASSERT (this->buffTab);
    -          ASSERT (0 < this->buffTabSize());
    -          ASSERT (this->nrO == this->nrI );
    -          ASSERT (this->nrO+this->nrI <= this->buffTabSize());
    -          ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]);
    -          BuffHandle           *inH = this->buffTab->inHandles;
    -          BuffHandle::PBuff *inBuff = this->buffTab->inBuffs;
    +          ASSERT (ivo.buffTab);
    +          ASSERT (0 < ivo.buffTabSize());
    +          ASSERT (ivo.nrO() == ivo.nrI() );
    +          ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
    +          ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
    +          BuffHandle           *inH = ivo.buffTab->inHandles;
    +          BuffHandle::PBuff *inBuff = ivo.buffTab->inBuffs;
               
    -          for (uint i = 0; i < this->nrI; ++i )
    +          for (uint i = 0; i < ivo.nrI(); ++i )
                 {
                   inBuff[i] =
    -                *(inH[i] = this->pullPredecessor(i)); // invoke predecessor
    +                *(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor
                   // now Input #i is ready...
                 }
    -          return NEXT::step (ivo);
    +          return NEXT::step (ivo); // note: passing down a ref to the ProcessInvocation
             }
         };
       
    @@ -212,23 +276,23 @@ namespace engine {
       struct ReadSource : NEXT
         {
           BuffHandle
    -      step (Invocation& ivo)
    +      step (Invocation& started_invocation)
             {
    -          this->createBuffTable();
    +          ProcessInvocation ivo (started_invocation);
               
    -          ASSERT (this->buffTab);
    -          ASSERT (0 < this->buffTabSize());
    -          ASSERT (this->nrO+this->nrI <= this->buffTabSize());
    -          ASSERT (this->buffTab->inHandles = &this->buffTab->handles[this->nrO]);
    -          BuffHandle           *inH  = this->buffTab->inHandles;
    -          BuffHandle           *outH = this->buffTab->handles;
    -          BuffHandle::PBuff *inBuff  = this->buffTab->inBuffs;
    -          BuffHandle::PBuff *outBuff = this->buffTab->buffers;
    +          ASSERT (ivo.buffTab);
    +          ASSERT (0 < ivo.buffTabSize());
    +          ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
    +          ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
    +          BuffHandle           *inH  = ivo.buffTab->inHandles;
    +          BuffHandle           *outH = ivo.buffTab->handles;
    +          BuffHandle::PBuff *inBuff  = ivo.buffTab->inBuffs;
    +          BuffHandle::PBuff *outBuff = ivo.buffTab->buffers;
               
    -          for (uint i = 0; i < this->nrI; ++i )
    +          for (uint i = 0; i < ivo.nrI(); ++i )
                 {
                   inBuff[i] = outBuff[i] =
    -                *(inH[i] = outH[i] = this->getSource(i)); // TODO: how to access source nodes???
    +                *(inH[i] = outH[i] = this->getSource(ivo,i));
                   // now Input #i is ready...
                 }
               return NEXT::step (ivo);
    @@ -236,97 +300,71 @@ namespace engine {
         };
       
       
    -  template
    -  struct AllocOutput
    +  template
    +  struct AllocOutput : NEXT
         {
           BuffHandle 
           step (Invocation& ivo)
             {
    -          ASSERT (this->buffTab);
    -          ASSERT (this->nrO < this->buffTabSize());
    -          BuffHandle           *outH = this->buffTab->handles;
    -          BuffHandle::PBuff *outBuff = this->buffTab->buffers;
    +          ASSERT (ivo.buffTab);
    +          ASSERT (ivo.nrO() < ivo.buffTabSize());
    +          BuffHandle           *outH = ivo.buffTab->handles;
    +          BuffHandle::PBuff *outBuff = ivo.buffTab->buffers;
               
    -          for (uint i = 0; i < this->nrO; ++i )
    +          for (uint i = 0; i < ivo.nrO(); ++i )
                 {
                   outBuff[i] =
    -                 *(outH[i] = allocateBuffer (i));
    +                 *(outH[i] = ivo.allocateBuffer (i));
                   // now Output buffer for channel #i is available...
                 }
               return NEXT::step (ivo);
             }
    -      
    -    private:
    -      BuffHandle const&
    -      allocateBuffer (uint outCh)
    -        {
    -          BUFFSRC::getBufferProvider(this).allocateBuffer(
    -                          this->getBufferDescriptor(outCh));
    -        }
    -    };
    -  
    -  
    -  enum OutBuffProvider { PARENT, CACHE };
    -  
    -  template
    -  struct OutBuffSource ;
    -  
    -  template<>
    -  struct OutBuffSource
    -    {
    -      static State& getBufferProvider (Invocation& thisState) { return thisState.parent_; }
    -    };
    -  
    -  template<>
    -  struct OutBuffSource
    -    {
    -      static State& getBufferProvider (Invocation& thisState) { return thisState.current_; }
         };
       
       
       template
    -  struct ProcessData
    +  struct ProcessData : NEXT
         {
           BuffHandle 
           step (Invocation& ivo)
             {
    -          ASSERT (this->buffTab);
    -          ASSERT (this->nrO+this->nrI <= this->buffTabSize());
    -          ASSERT (this->validateBuffers());
    +          ASSERT (ivo.buffTab);
    +          ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
    +          ASSERT (this->validateBuffers(ivo));
               
                // Invoke our own process() function, providing the buffer array
    -          this->wiring_.processFunction (this->buffTab->buffers);
    +          ivo.wiring.processFunction (ivo.buffTab->buffers);
               
               return NEXT::step (ivo);
             }
         };
       
       template
    -  struct FeedCache
    +  struct FeedCache : NEXT
         {
           BuffHandle 
           step (Invocation& ivo)
             {
               // declare all Outputs as finished
    -          this->current_.isCalculated(this->buffTab->handles, 
    -                                      this->nrO);
    +          ivo.isCalculated(ivo.buffTab->handles, 
    +                           ivo.nrO());
               
               return NEXT::step (ivo);
             }
         };
       
       template
    -  struct ReleaseBuffers
    -    {
    +  struct ReleaseBuffers : NEXT                     /////////////////TODO: couldn't this be done automatically by BuffTab's dtor??
    +    {                                             /////////////////       this would require BuffHandle to be a smart ref....
           BuffHandle 
           step (Invocation& ivo)
             {
               // all buffers besides the required Output no longer needed
    -          this->current_.releaseBuffers(this->buffTab->handles, 
    -                                        this->buffTabSize(), 
    -                                        this->requiredOutputNr);
    +          this->releaseBuffers(ivo.buffTab->handles, 
    +                               ivo.buffTabSize(), 
    +                               ivo.outNr);
               
    -          return this->buffTab->outH[this->requiredOutputNr];
    +          return ivo.buffTab->outH[this->requiredOutputNr];
             }
         };
       
    @@ -335,9 +373,8 @@ namespace engine {
       
       /* === declare the possible Assembly of these elementary steps === */
       
    -  template
    -  struct Strategy ;
    -
    +  template
    +  struct OutBuffSource{ static const StateAdapter::BufferProvider VALUE = buffPro; };
       
       template
       struct SelectBuffProvider;
    @@ -348,23 +385,26 @@ namespace engine {
       template<> struct SelectBuffProvider<>                : OutBuffSource{ };
     
       
    +  template
    +  struct Strategy ;
    +
       using lumiera::typelist::Config;
       
    -  template
    -  struct Strategy< Config >
    +  template
    +  struct Strategy< Config >
         : QueryCache<
            PullInput<
    -        AllocOutput,
    +        AllocOutput<
              ProcessData<
               FeedCache<
                ReleaseBuffers< 
                 OperationBase > > > > > >
         { };
       
    -  template
    -  struct Strategy< Config >
    +  template
    +  struct Strategy< Config >
         : PullInput<
    -       AllocOutput,
    +       AllocOutput<
             ProcessData<
               ReleaseBuffers< 
                OperationBase > > > > 
    @@ -383,7 +423,7 @@ namespace engine {
       template<>
       struct Strategy< Config >
         : ReadSource<
    -       AllocOutput,
    +       AllocOutput<
             ProcessData<                       // wiring_.processFunction is supposed to do just buffer copying here 
              ReleaseBuffers< 
               OperationBase > > > >  
    diff --git a/src/proc/state.hpp b/src/proc/state.hpp
    index 84732029d..cd98a39b8 100644
    --- a/src/proc/state.hpp
    +++ b/src/proc/state.hpp
    @@ -30,7 +30,6 @@
     #include "proc/lumiera.hpp"
     #include "common/frameid.hpp"
     #include "proc/engine/buffhandle.hpp"
    -#include "proc/engine/bufftable.hpp"
     
     #include 
     
    @@ -40,7 +39,7 @@ namespace engine {
       using lumiera::FrameID;
     
       class StateAdapter;
    -  
    +  class BuffTableStorage;
       
       class State
         {
    
    From 3ec69c539c642c332f12192085f6987cd398362d Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Sun, 20 Jul 2008 15:00:39 +0200
    Subject: [PATCH 10/40] WIP resolved remaining inconsistencies
    
    ---
     src/proc/engine/nodeoperation.hpp | 257 +++++++++++++++---------------
     1 file changed, 128 insertions(+), 129 deletions(-)
    
    diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
    index 6c0092edd..c52c48618 100644
    --- a/src/proc/engine/nodeoperation.hpp
    +++ b/src/proc/engine/nodeoperation.hpp
    @@ -65,7 +65,6 @@
     namespace engine {
       
       
    -
       /**
        * Adapter to shield the ProcNode from the actual buffer management,
        * allowing the processing function within ProcNode to use logical
    @@ -87,8 +86,7 @@ namespace engine {
             { }
           
           virtual State& getCurrentImplementation () { return current_; }
    -
    -      typedef State& (StateAdapter::*BufferProvider);
    +      
           
           
         public: /* === proxying the State interface === */
    @@ -99,72 +97,84 @@ namespace engine {
           
           virtual BuffHandle fetch (FrameID const& fID)     { return current_.fetch (fID); }
           
    -      
           // note: allocateBuffer()  is choosen specifically based on the actual node wiring
           
    -      
    -    protected: /* === API for use by the node operation pull() call === */
    -      
    -      BufferDescriptor
    -      getBufferDescriptor (uint chan)
    -        {
    -          TODO ("determine BufferDescriptor based on WiringDescriptor channel info");
    -        }
         };
       
    -    
    +  
    +  
    +  
    +  
       /**
        * Invocation context state.
        * A ref to this type is carried through the chain of NEXT::step() functions
        * which form the actual invocation sequence. The various operations in this sequence
    -   * access the context via the references in this struct, and also using the inherited
    -   * public State intercace. The object instance actually used as Invocation is created
    -   * on the stack and parametrized according to the necessities of the invocation
    -   * sequence actually configured. This real object instance comes in two flavours:
    -   * - a intended/partial invocation (which may be short-circuited due to Cache hit)
    -   * - a complete invocation, including storage for managing the buffer handles.
    +   * access the context via the references in this struct, while also using the inherited
    +   * public State interface. The object instance actually used as Invocation is created
    +   * on the stack and parametrized according to the necessities of the invocation sequence
    +   * actually configured. Initially, this real instance is configured without BuffTable,
    +   * because the invocation may be short-circuited due to Cache hit. Otherwise, when
    +   * the invocation sequence actually prepares to call the process function of this
    +   * ProcNode, a buffer table chunk is allocated by the StateProxy and wired in.
        */
       struct Invocation
         : StateAdapter
         {
           WiringDescriptor const& wiring;
    -      BuffTable& buffTab;
           const uint outNr;
           
    +      BuffTable* buffTab;
    +      
         protected:
    -      /** creates a new invocation context state, 
    -       *  configuring it as intended/partial invocation without BuffTable */
    -      Invocation (State& callingProcess, WiringDescriptor const& w, BufferProvider buffSrc, uint o)
    +      /** creates a new invocation context state, without BuffTable */
    +      Invocation (State& callingProcess, WiringDescriptor const& w, uint o)
             : StateAdapter(callingProcess),
               wiring(w), outNr(o),
    -          bufferProvider_(buffSrc)
    +          buffTab(0)
             { }
           
    -      /** used to build a complete invocation, including a buffer table,
    -          based on an existing intended/partial invocation */
    -      Invocation (Invocation const& currInvocation, BuffTable& localBuffTab)
    -        : StateAdapter(currInvocation),
    -          wiring(currInvocation.wiring),
    -          buffTab(localBuffTab),
    -          outNr(currInvocation.outNr),
    -          bufferProvider_(currInvocation.bufferProvider_)
    -        { }
    +      const uint nrO()          const { return wiring.getNrO(); }
    +      const uint nrI()          const { return wiring.getNrI(); }
    +      const uint buffTabSize()  const { return nrO()+nrI(); }
           
    -      /** configuration of the actual entity in charge of allocating Buffers.
    -       *  depending on the configured call sequence, this may be a parent StateAdapter,
    -       *  or the backend in case we want to fade the results into the Cache  */
    -      const BufferProvider bufferProvider_;
    +      /** setup the link to an externally allocated buffer table */
    +      void setBuffTab (BuffTable* b) { this->buffTab = b; }
           
    -    public:
    -      BuffHandle const&
    -      allocateBuffer (uint outChannel)
    +      bool
    +      buffTab_isConsistent ()
             {
    -          return this->*bufferProvider_.allocateBuffer(
    -                   this->getBufferDescriptor(outChannel));
    +          return (buffTab)
    +              && (0 < buffTabSize())
    +              && (nrO()+nrI() <= buffTabSize())
    +              && (buffTab->inBuff   == &buffTab->outBuff[nrO()]  )
    +              && (buffTab->inHandle == &buffTab->outHandle[nrO()])
    +               ;
             }
    +      
    +      
         };
    -
    -  
    +    
    +    
    +    struct AllocBufferFromParent  ///< using the parent StateAdapter for buffer allocations
    +      : Invocation
    +      {
    +        AllocBufferFromParent (State& sta, WiringDescriptor const& w, const uint outCh) 
    +          : Invocation(sta, w, outCh) {}
    +        
    +        virtual BuffHandle
    +        allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); }
    +      };
    +    
    +    struct AllocBufferFromCache   ///< using the global current State, which will delegate to Cache
    +      : Invocation
    +      {
    +        AllocBufferFromCache (State& sta, WiringDescriptor const& w, const uint outCh) 
    +          : Invocation(sta, w, outCh) {}
    +        
    +        virtual BuffHandle
    +        allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
    +      };
    +    
       
       /**
        * The real invocation context state implementation. It is created
    @@ -177,17 +187,17 @@ namespace engine {
        * with a concrete type according to the WiringDescriptor of the node to pull.
        * This concrete type encodes a calculation Strategy, which is assembled
        * as a chain of policy templates on top of OperationBase. For each of the
    -   * possible configuratons we define such a chain (see bottom of this header).
    +   * possible configuratons we define such a chain (see bottom of nodeoperation.hpp).
        * The WiringFactory defined in nodewiring.cpp actually drives the instantiation
        * of all those possible combinations.
        */
    -  template
    -  class ActualInvocationOrder
    -    : public Invocation
    +  template
    +  class ActualInvocationProcess
    +    : public BufferProvider
         {
         public:
    -      InvocationImpl (State& callingProcess, WiringDescriptor const& w, const uint outCh)
    -        : Invocation(callingProcess, w, buffPro, outCh)
    +      ActualInvocationProcess (State& callingProcess, WiringDescriptor const& w, const uint outCh)
    +        : Invocation(callingProcess, w, outCh)
             { }
           
           /** contains the details of Cache query and recursive calls
    @@ -199,28 +209,10 @@ namespace engine {
               return Strategy::step (*this);
             }
         };
    -
       
    -  /** 
    -   * Extends the invocation context state by a table of buffer pointers.
    -   * This extension is used whenever the call sequence goes into actually
    -   * invoking a process() function, which necessiates to build a array of
    -   * buffer pointers and to allocate buffers to hold the data to be worked on.
    -   * It is implemented as decorating an already created "partial invocation",
    -   * just adding the allocation of a Table Chunk for organizing the buffers.
    -   */
    -  class ProcessInvocation
    -    : protected BuffTableChunk,
    -      public Invocation
    -    {
    -    public:
    -      ProcessInvocation (Invocation const& currInvocation)
    -        : BuffTableChunk(currInvocation.wiring, currInvocation.getBuffTableStorage()),
    -          Invocation(currInvocation, static_cast(*this))
    -        { }
    -    };
    -    
    -    
    +  
    +  
    +  
       /**
        * Collection of functions used to build up the invocation sequence.
        */
    @@ -243,23 +235,32 @@ namespace engine {
                 return NEXT::step (ivo);
             }
         };
    +  
    +  
    +  template
    +  struct AllocBufferTable : NEXT
    +    {
    +      BuffHandle
    +      step (Invocation& ivo)
    +        {
    +          BuffTableChunk buffTab (ivo.wiring, ivo.getBuffTableStorage());
    +          ivo.setBuffTab(&buffTab);
    +          ASSERT (ivo.buffTab);
    +          ASSERT (ivo.buffTab_isConsistent());
               
    +          return NEXT::step (ivo);
    +        }
    +    };
    +  
       
       template
       struct PullInput : NEXT
         {
           BuffHandle
    -      step (Invocation& started_invocation)
    +      step (Invocation& ivo)
             {
    -          ProcessInvocation ivo (started_invocation);
    -          
    -          ASSERT (ivo.buffTab);
    -          ASSERT (0 < ivo.buffTabSize());
    -          ASSERT (ivo.nrO() == ivo.nrI() );
    -          ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
    -          ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
    -          BuffHandle           *inH = ivo.buffTab->inHandles;
    -          BuffHandle::PBuff *inBuff = ivo.buffTab->inBuffs;
    +          BuffHandle        *   inH = ivo.buffTab->inHandle;
    +          BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff;
               
               for (uint i = 0; i < ivo.nrI(); ++i )
                 {
    @@ -276,18 +277,14 @@ namespace engine {
       struct ReadSource : NEXT
         {
           BuffHandle
    -      step (Invocation& started_invocation)
    +      step (Invocation& ivo)
             {
    -          ProcessInvocation ivo (started_invocation);
    +          BuffHandle           *inH  = ivo.buffTab->inHandle;
    +          BuffHandle           *outH = ivo.buffTab->outHandle;
    +          BuffHandle::PBuff *inBuff  = ivo.buffTab->inBuff;
    +          BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff;
               
    -          ASSERT (ivo.buffTab);
    -          ASSERT (0 < ivo.buffTabSize());
    -          ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
    -          ASSERT (ivo.buffTab->inHandles == &ivo.buffTab->handles[ivo.nrO()]);
    -          BuffHandle           *inH  = ivo.buffTab->inHandles;
    -          BuffHandle           *outH = ivo.buffTab->handles;
    -          BuffHandle::PBuff *inBuff  = ivo.buffTab->inBuffs;
    -          BuffHandle::PBuff *outBuff = ivo.buffTab->buffers;
    +          ASSERT (ivo.nrO() == ivo.nrI() );
               
               for (uint i = 0; i < ivo.nrI(); ++i )
                 {
    @@ -308,13 +305,13 @@ namespace engine {
             {
               ASSERT (ivo.buffTab);
               ASSERT (ivo.nrO() < ivo.buffTabSize());
    -          BuffHandle           *outH = ivo.buffTab->handles;
    -          BuffHandle::PBuff *outBuff = ivo.buffTab->buffers;
    +          BuffHandle           *outH = ivo.buffTab->outHandle;
    +          BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff;
               
               for (uint i = 0; i < ivo.nrO(); ++i )
                 {
                   outBuff[i] =
    -                 *(outH[i] = ivo.allocateBuffer (i));
    +                 *(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType);
                   // now Output buffer for channel #i is available...
                 }
               return NEXT::step (ivo);
    @@ -329,11 +326,12 @@ namespace engine {
           step (Invocation& ivo)
             {
               ASSERT (ivo.buffTab);
    -          ASSERT (ivo.nrO()+ivo.nrI() <= ivo.buffTabSize());
    +          ASSERT (ivo.buffTab_isConsistent());
               ASSERT (this->validateBuffers(ivo));
               
    -           // Invoke our own process() function, providing the buffer array
    -          ivo.wiring.processFunction (ivo.buffTab->buffers);
    +           // Invoke our own process() function,
    +          //  providing the array of outBuffer+inBuffer ptrs
    +          ivo.wiring.processFunction (ivo.buffTab->outBuff);
               
               return NEXT::step (ivo);
             }
    @@ -346,8 +344,8 @@ namespace engine {
           step (Invocation& ivo)
             {
               // declare all Outputs as finished
    -          ivo.isCalculated(ivo.buffTab->handles, 
    -                           ivo.nrO());
    +          ivo.is_calculated(ivo.buffTab->outHandle, 
    +                            ivo.nrO());
               
               return NEXT::step (ivo);
             }
    @@ -360,29 +358,26 @@ namespace engine {
           step (Invocation& ivo)
             {
               // all buffers besides the required Output no longer needed
    -          this->releaseBuffers(ivo.buffTab->handles, 
    +          this->releaseBuffers(ivo.buffTab->outHandle, 
                                    ivo.buffTabSize(), 
                                    ivo.outNr);
               
    -          return ivo.buffTab->outH[this->requiredOutputNr];
    +          return ivo.buffTab->outHandle[ivo.outNr];
             }
         };
       
       
    -
    +  
       
       /* === declare the possible Assembly of these elementary steps === */
       
    -  template
    -  struct OutBuffSource{ static const StateAdapter::BufferProvider VALUE = buffPro; };
    -  
       template
       struct SelectBuffProvider;
       
    -  template<> struct SelectBuffProvider         : OutBuffSource { };
    -  template<> struct SelectBuffProvider : OutBuffSource{ };
    -  template<> struct SelectBuffProvider : OutBuffSource { };
    -  template<> struct SelectBuffProvider<>                : OutBuffSource{ };
    +  template<> struct SelectBuffProvider         : AllocBufferFromCache { };
    +  template<> struct SelectBuffProvider : AllocBufferFromParent{ };
    +  template<> struct SelectBuffProvider : AllocBufferFromCache { };
    +  template<> struct SelectBuffProvider<>                : AllocBufferFromParent{ };
     
       
       template
    @@ -393,28 +388,31 @@ namespace engine {
       template
       struct Strategy< Config >
         : QueryCache<
    -       PullInput<
    -        AllocOutput<
    -         ProcessData<
    -          FeedCache<
    -           ReleaseBuffers< 
    -            OperationBase > > > > > >
    +       AllocBufferTable<
    +        PullInput<
    +         AllocOutput<
    +          ProcessData<
    +           FeedCache<
    +            ReleaseBuffers< 
    +             OperationBase > > > > > > >
         { };
       
       template
       struct Strategy< Config >
    -    : PullInput<
    -       AllocOutput<
    -        ProcessData<
    +    : AllocBufferTable<
    +       PullInput<
    +        AllocOutput<
    +         ProcessData<
               ReleaseBuffers< 
    -           OperationBase > > > > 
    +           OperationBase > > > > >
         { };
       
       template<>
       struct Strategy< Config<> >
    -    : ReadSource<
    -       ReleaseBuffers< 
    -        OperationBase > >  
    +    : AllocBufferTable<
    +       ReadSource<
    +        ReleaseBuffers< 
    +         OperationBase > > >  
         { };
       
       template<>
    @@ -422,15 +420,16 @@ namespace engine {
       
       template<>
       struct Strategy< Config >
    -    : ReadSource<
    -       AllocOutput<
    -        ProcessData<                       // wiring_.processFunction is supposed to do just buffer copying here 
    -         ReleaseBuffers< 
    -          OperationBase > > > >  
    +    : AllocBufferTable<
    +       ReadSource<
    +        AllocOutput<
    +         ProcessData<                       // wiring_.processFunction is supposed to do just buffer copying here 
    +          ReleaseBuffers< 
    +           OperationBase > > > > >  
         { };
    -
       
    -    
    +  
    +  
       
     } // namespace engine
     #endif
    
    From 9a34d234aef013fe982ec355ec4e5075508b650b Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Sun, 20 Jul 2008 20:08:08 +0200
    Subject: [PATCH 11/40] WIP pass compiler, after filling in some more stubs
    
    ---
     src/proc/engine/buffhandle.hpp     |  15 ++
     src/proc/engine/nodeinvocation.hpp | 216 +++++++++++++++++++++++++++++
     src/proc/engine/nodeoperation.hpp  | 189 ++++---------------------
     src/proc/engine/nodewiring.cpp     |   3 +-
     src/proc/engine/procnode.hpp       |  13 ++
     5 files changed, 270 insertions(+), 166 deletions(-)
     create mode 100644 src/proc/engine/nodeinvocation.hpp
    
    diff --git a/src/proc/engine/buffhandle.hpp b/src/proc/engine/buffhandle.hpp
    index 60ca7758d..51ce1c135 100644
    --- a/src/proc/engine/buffhandle.hpp
    +++ b/src/proc/engine/buffhandle.hpp
    @@ -84,6 +84,21 @@ namespace engine {
         };
       
       
    +  class ProcNode;
    +  typedef ProcNode* PNode;
    +  
    +  
    +  struct ChannelDescriptor  ///////TODO collapse this with BufferDescriptor?
    +    {
    +      BufferDescriptor bufferType;
    +    };
    +  
    +  struct InChanDescriptor : ChannelDescriptor
    +    {
    +      PNode dataSrc;    ///< the ProcNode to pull this input from
    +      uint srcChannel; ///<  output channel to use on the predecessor node
    +    };
    +  
       
     } // namespace engine
     #endif
    diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp
    new file mode 100644
    index 000000000..2b5c0afbb
    --- /dev/null
    +++ b/src/proc/engine/nodeinvocation.hpp
    @@ -0,0 +1,216 @@
    +/*
    +  NODEINVOCATION.hpp  -  Organize the invocation state within a single pull() call
    + 
    +  Copyright (C)         Lumiera.org
    +    2008,               Hermann Vosseler 
    + 
    +  This program is free software; you can redistribute it and/or
    +  modify it under the terms of the GNU General Public License as
    +  published by the Free Software Foundation; either version 2 of the
    +  License, or (at your option) any later version.
    + 
    +  This program is distributed in the hope that it will be useful,
    +  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +  GNU General Public License for more details.
    + 
    +  You should have received a copy of the GNU General Public License
    +  along with this program; if not, write to the Free Software
    +  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + 
    +*/
    +
    +/** @file nodeinvocation.hpp
    + ** Organize the state related to the invocation of s single ProcNode::pull() call
    + ** This header defines part of the "glue" which holds together the render node network
    + ** and enables to pull a result frames from the nodes. Doing so requires some invocation
    + ** local state tobe maintained, especially a table of buffers used to carry out the
    + ** calculations. Further, getting the input buffers filled requires to issue recursive
    + ** \c pull() calls, which on the whole creates a stack-like assembly of local invocation
    + ** state.
    + ** The actual steps to be carried out for a \c pull() call are dependant on the configuration
    + ** of the node to pull. Each node has been preconfigured by the builder with a WiringDescriptor
    + ** and a concrete type of a StateAdapter. The actual sequence of steps is defined in the header
    + ** nodeoperation.hpp out of a set of basic operation steps. These steps all use the passed in
    + ** Invocation object (a sub-interface of StateAdapter) to access the various aspects of the
    + ** invocation state.
    + ** 
    + ** \par composition of the Invocation State
    + ** For each individual ProcNode#pull() call, the WiringAdapter#callDown() builds an StateAdapter
    + ** instance directly on the stack, holding the actual buffer pointers and state references. Using this
    + ** StateAdapter, the predecessor nodes are pulled. The way these operations are carried out is encoded
    + ** in the actual StateAdapter type known to the NodeWiring (WiringAdapter) instance. All of these actual
    + ** StateAdapter types are built as implementing the engine::State interface.
    + **
    + ** @see engine::ProcNode
    + ** @see engine::StateProxy
    + ** @see engine::BuffTable
    + ** @see nodewiring.hpp interface for building/wiring the nodes
    + **
    + */
    +
    +#ifndef ENGINE_NODEINVOCATION_H
    +#define ENGINE_NODEINVOCATION_H
    +
    +
    +#include "proc/state.hpp"
    +#include "proc/engine/procnode.hpp"
    +#include "proc/engine/buffhandle.hpp"
    +#include "proc/engine/bufftable.hpp"
    +
    +
    +
    +
    +namespace engine {
    +  
    +  
    +  /**
    +   * Adapter to shield the ProcNode from the actual buffer management,
    +   * allowing the processing function within ProcNode to use logical
    +   * buffer IDs. StateAdapter is created on the stack for each pull()
    +   * call, using setup/wiring data preconfigured by the builder.
    +   * Its job is to provide the actual implementation of the Cache
    +   * push / fetch and recursive downcall to render the source frames.
    +   */
    +  class StateAdapter
    +    : public State
    +    {
    +    protected:
    +      State& parent_;
    +      State& current_;
    +      
    +      StateAdapter (State& callingProcess) 
    +        : parent_ (callingProcess),
    +          current_(callingProcess.getCurrentImplementation())
    +        { }
    +      
    +      virtual State& getCurrentImplementation () { return current_; }
    +      
    +      
    +      
    +    public: /* === proxying the State interface === */
    +      
    +      virtual void releaseBuffer (BuffHandle& bh)       { current_.releaseBuffer (bh); }
    +      
    +      virtual void is_calculated (BuffHandle const& bh) { current_.is_calculated (bh); }
    +      
    +      virtual BuffHandle fetch (FrameID const& fID)     { return current_.fetch (fID); }
    +      
    +      // note: allocateBuffer()  is choosen specifically based on the actual node wiring
    +      
    +    };
    +  
    +  
    +  
    +  
    +  
    +  /**
    +   * Invocation context state.
    +   * A ref to this type is carried through the chain of NEXT::step() functions
    +   * which form the actual invocation sequence. The various operations in this sequence
    +   * access the context via the references in this struct, while also using the inherited
    +   * public State interface. The object instance actually used as Invocation is created
    +   * on the stack and parametrized according to the necessities of the invocation sequence
    +   * actually configured. Initially, this real instance is configured without BuffTable,
    +   * because the invocation may be short-circuited due to Cache hit. Otherwise, when
    +   * the invocation sequence actually prepares to call the process function of this
    +   * ProcNode, a buffer table chunk is allocated by the StateProxy and wired in.
    +   */
    +  struct Invocation
    +    : StateAdapter
    +    {
    +      WiringDescriptor const& wiring;
    +      const uint outNr;
    +      
    +      BuffTable* buffTab;
    +      
    +    protected:
    +      /** creates a new invocation context state, without BuffTable */
    +      Invocation (State& callingProcess, WiringDescriptor const& w, uint o)
    +        : StateAdapter(callingProcess),
    +          wiring(w), outNr(o),
    +          buffTab(0)
    +        { }
    +      
    +      const uint nrO()          const { return wiring.getNrO(); }
    +      const uint nrI()          const { return wiring.getNrI(); }
    +      const uint buffTabSize()  const { return nrO()+nrI(); }
    +      
    +      /** setup the link to an externally allocated buffer table */
    +      void setBuffTab (BuffTable* b) { this->buffTab = b; }
    +      
    +      bool
    +      buffTab_isConsistent ()
    +        {
    +          return (buffTab)
    +              && (0 < buffTabSize())
    +              && (nrO()+nrI() <= buffTabSize())
    +              && (buffTab->inBuff   == &buffTab->outBuff[nrO()]  )
    +              && (buffTab->inHandle == &buffTab->outHandle[nrO()])
    +               ;
    +        }
    +      
    +      
    +    };
    +    
    +    
    +    struct AllocBufferFromParent  ///< using the parent StateAdapter for buffer allocations
    +      : Invocation
    +      {
    +        AllocBufferFromParent (State& sta, WiringDescriptor const& w, const uint outCh) 
    +          : Invocation(sta, w, outCh) {}
    +        
    +        virtual BuffHandle
    +        allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); }
    +      };
    +    
    +    struct AllocBufferFromCache   ///< using the global current State, which will delegate to Cache
    +      : Invocation
    +      {
    +        AllocBufferFromCache (State& sta, WiringDescriptor const& w, const uint outCh) 
    +          : Invocation(sta, w, outCh) {}
    +        
    +        virtual BuffHandle
    +        allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
    +      };
    +    
    +  
    +  /**
    +   * The real invocation context state implementation. It is created
    +   * by the NodeWiring (WiringDescriptor) of the processing node which
    +   * is pulled by this invocation, hereby using the internal configuration
    +   * information to guide the selecton of the real call sequence 
    +   * 
    +   * \par assembling the call sequence implementation
    +   * Each ProcNode#pull() call creates such a StateAdapter subclass on the stack,
    +   * with a concrete type according to the WiringDescriptor of the node to pull.
    +   * This concrete type encodes a calculation Strategy, which is assembled
    +   * as a chain of policy templates on top of OperationBase. For each of the
    +   * possible configuratons we define such a chain (see bottom of nodeoperation.hpp).
    +   * The WiringFactory defined in nodewiring.cpp actually drives the instantiation
    +   * of all those possible combinations.
    +   */
    +  template
    +  class ActualInvocationProcess
    +    : public BufferProvider
    +    {
    +    public:
    +      ActualInvocationProcess (State& callingProcess, WiringDescriptor const& w, const uint outCh)
    +        : Invocation(callingProcess, w, outCh)
    +        { }
    +      
    +      /** contains the details of Cache query and recursive calls
    +       *  to the predecessor node(s), eventually followed by the 
    +       *  ProcNode::process() callback
    +       */
    +      BuffHandle retrieve ()
    +        {
    +          return Strategy::step (*this);
    +        }
    +    };
    +  
    +  
    +  
    +  
    +} // namespace engine
    +#endif
    diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp
    index c52c48618..144050d39 100644
    --- a/src/proc/engine/nodeoperation.hpp
    +++ b/src/proc/engine/nodeoperation.hpp
    @@ -22,28 +22,33 @@
     
     /** @file nodeoperation.hpp
      ** Chunks of operation for invoking the rendernodes.
    - ** This header defines the "glue" which holds together the render node network
    + ** This header defines part of the "glue" which holds together the render node network
      ** and enables to pull a result frames from the nodes. Especially, the aspect of
    - ** buffer management is covered here. Each node has been preconfigured by the builder
    - ** with a WiringDescriptor and a concrete type of a StateAdapter. These concrete
    - ** StateAdapter objects are assembled out of the building blocks defined in this header,
    - ** depending on the desired mode of operation. Any node can be built to 
    + ** buffer management and cache query is covered here. Each node has been preconfigured by
    + ** the builder with a WiringDescriptor and a concrete type of a StateAdapter, including
    + ** a specific Configuration, because the node can be built to 
      ** - participate in the Caching or ignore the cache
      ** - actually process a result or just pull frames from a source
      ** - employ in-Place calculations or use separate in/out buffers
      ** Additionally, each node may have a given number of input/output pins, expecting to
      ** be provided with buffers holding a specific kind of data.
      ** 
    - ** \par composition of the StateAdapter
    - ** For each individual ProcNode#pull() call, the WiringAdapter#callDown() builds an StateAdapter
    - ** instance directly on the stack, holding the actual buffer pointers and state references. Using this
    + ** \par composition of the invocation Strategy
    + ** For each individual ProcNode#pull() call, the WiringAdapter#callDown() builds an Invocation state
    + ** instance directly on the stack, holding references to the actual buffer pointers and state. Using this
      ** StateAdapter, the predecessor nodes are pulled. The way these operations are carried out is encoded
    - ** in the actual StateAdapter type known to the NodeWiring (WiringAdapter) instance. All of these actual
    - ** StateAdapter types are built as implementing the engine::State interface, on top of the InvocationStateBase
    - ** and inheriting from a chain of strategy classes (single inheritance, mostly \em no virtual functions).
    + ** in the actual type of Strategy, which is defined at the bottom of this header. Each Strategy is a chain
    + ** of elementary operations invoking each other (\c NEXT::step(invocation) ). Notably, all those possible
    + ** configurations are pre-build while compiling (it's a small number below 32 configuration instance).
    + ** To be able to select the Strategy for each configuration, we need a Factory (ConfigSelector defined in
    + ** nodewiringconfig). which is actually instantiated and used in nodewiring.cpp, which is the object
    + ** file holding all those instantiations. 
      **
      ** @see engine::ProcNode
    - ** @see engine::StateProxy
    + ** @see engine::Invocation
    + ** @see engine::State
    + ** @see engine::NodeFactory
    + ** @see nodewiringconfig.hpp
      ** @see nodewiring.hpp interface for building/wiring the nodes
      **
      */
    @@ -56,8 +61,8 @@
     #include "proc/engine/procnode.hpp"
     #include "proc/engine/buffhandle.hpp"
     #include "proc/engine/bufftable.hpp"
    +#include "proc/engine/nodeinvocation.hpp"
     #include "proc/engine/nodewiringconfig.hpp"
    -#include "lib/appconfig.hpp"
     
     
     
    @@ -65,154 +70,6 @@
     namespace engine {
       
       
    -  /**
    -   * Adapter to shield the ProcNode from the actual buffer management,
    -   * allowing the processing function within ProcNode to use logical
    -   * buffer IDs. StateAdapter is created on the stack for each pull()
    -   * call, using setup/wiring data preconfigured by the builder.
    -   * Its job is to provide the actual implementation of the Cache
    -   * push / fetch and recursive downcall to render the source frames.
    -   */
    -  class StateAdapter
    -    : public State
    -    {
    -    protected:
    -      State& parent_;
    -      State& current_;
    -      
    -      StateAdapter (State& callingProcess) 
    -        : parent_ (callingProcess),
    -          current_(callingProcess.getCurrentImplementation())
    -        { }
    -      
    -      virtual State& getCurrentImplementation () { return current_; }
    -      
    -      
    -      
    -    public: /* === proxying the State interface === */
    -      
    -      virtual void releaseBuffer (BuffHandle& bh)       { current_.releaseBuffer (bh); }
    -      
    -      virtual void is_calculated (BuffHandle const& bh) { current_.is_calculated (bh); }
    -      
    -      virtual BuffHandle fetch (FrameID const& fID)     { return current_.fetch (fID); }
    -      
    -      // note: allocateBuffer()  is choosen specifically based on the actual node wiring
    -      
    -    };
    -  
    -  
    -  
    -  
    -  
    -  /**
    -   * Invocation context state.
    -   * A ref to this type is carried through the chain of NEXT::step() functions
    -   * which form the actual invocation sequence. The various operations in this sequence
    -   * access the context via the references in this struct, while also using the inherited
    -   * public State interface. The object instance actually used as Invocation is created
    -   * on the stack and parametrized according to the necessities of the invocation sequence
    -   * actually configured. Initially, this real instance is configured without BuffTable,
    -   * because the invocation may be short-circuited due to Cache hit. Otherwise, when
    -   * the invocation sequence actually prepares to call the process function of this
    -   * ProcNode, a buffer table chunk is allocated by the StateProxy and wired in.
    -   */
    -  struct Invocation
    -    : StateAdapter
    -    {
    -      WiringDescriptor const& wiring;
    -      const uint outNr;
    -      
    -      BuffTable* buffTab;
    -      
    -    protected:
    -      /** creates a new invocation context state, without BuffTable */
    -      Invocation (State& callingProcess, WiringDescriptor const& w, uint o)
    -        : StateAdapter(callingProcess),
    -          wiring(w), outNr(o),
    -          buffTab(0)
    -        { }
    -      
    -      const uint nrO()          const { return wiring.getNrO(); }
    -      const uint nrI()          const { return wiring.getNrI(); }
    -      const uint buffTabSize()  const { return nrO()+nrI(); }
    -      
    -      /** setup the link to an externally allocated buffer table */
    -      void setBuffTab (BuffTable* b) { this->buffTab = b; }
    -      
    -      bool
    -      buffTab_isConsistent ()
    -        {
    -          return (buffTab)
    -              && (0 < buffTabSize())
    -              && (nrO()+nrI() <= buffTabSize())
    -              && (buffTab->inBuff   == &buffTab->outBuff[nrO()]  )
    -              && (buffTab->inHandle == &buffTab->outHandle[nrO()])
    -               ;
    -        }
    -      
    -      
    -    };
    -    
    -    
    -    struct AllocBufferFromParent  ///< using the parent StateAdapter for buffer allocations
    -      : Invocation
    -      {
    -        AllocBufferFromParent (State& sta, WiringDescriptor const& w, const uint outCh) 
    -          : Invocation(sta, w, outCh) {}
    -        
    -        virtual BuffHandle
    -        allocateBuffer (BufferDescriptor const& bd) { return parent_.allocateBuffer(bd); }
    -      };
    -    
    -    struct AllocBufferFromCache   ///< using the global current State, which will delegate to Cache
    -      : Invocation
    -      {
    -        AllocBufferFromCache (State& sta, WiringDescriptor const& w, const uint outCh) 
    -          : Invocation(sta, w, outCh) {}
    -        
    -        virtual BuffHandle
    -        allocateBuffer (BufferDescriptor const& bd) { return current_.allocateBuffer(bd); }
    -      };
    -    
    -  
    -  /**
    -   * The real invocation context state implementation. It is created
    -   * by the NodeWiring (WiringDescriptor) of the processing node which
    -   * is pulled by this invocation, hereby using the internal configuration
    -   * information to guide the selecton of the real call sequence 
    -   * 
    -   * \par assembling the call sequence implementation
    -   * Each ProcNode#pull() call creates such a StateAdapter subclass on the stack,
    -   * with a concrete type according to the WiringDescriptor of the node to pull.
    -   * This concrete type encodes a calculation Strategy, which is assembled
    -   * as a chain of policy templates on top of OperationBase. For each of the
    -   * possible configuratons we define such a chain (see bottom of nodeoperation.hpp).
    -   * The WiringFactory defined in nodewiring.cpp actually drives the instantiation
    -   * of all those possible combinations.
    -   */
    -  template
    -  class ActualInvocationProcess
    -    : public BufferProvider
    -    {
    -    public:
    -      ActualInvocationProcess (State& callingProcess, WiringDescriptor const& w, const uint outCh)
    -        : Invocation(callingProcess, w, outCh)
    -        { }
    -      
    -      /** contains the details of Cache query and recursive calls
    -       *  to the predecessor node(s), eventually followed by the 
    -       *  ProcNode::process() callback
    -       */
    -      BuffHandle retrieve ()
    -        {
    -          return Strategy::step (*this);
    -        }
    -    };
    -  
    -  
    -  
    -  
       /**
        * Collection of functions used to build up the invocation sequence.
        */
    @@ -311,7 +168,7 @@ namespace engine {
               for (uint i = 0; i < ivo.nrO(); ++i )
                 {
                   outBuff[i] =
    -                 *(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType);
    +                 *(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType));
                   // now Output buffer for channel #i is available...
                 }
               return NEXT::step (ivo);
    @@ -343,9 +200,11 @@ namespace engine {
           BuffHandle 
           step (Invocation& ivo)
             {
    -          // declare all Outputs as finished
    -          ivo.is_calculated(ivo.buffTab->outHandle, 
    -                            ivo.nrO());
    +          for (uint i = 0; i < ivo.nrO(); ++i )
    +            {
    +              // declare all Outputs as finished
    +              ivo.is_calculated(ivo.buffTab->outHandle[i]);
    +            }
               
               return NEXT::step (ivo);
             }
    diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp
    index 023253a0e..a9cafa1a8 100644
    --- a/src/proc/engine/nodewiring.cpp
    +++ b/src/proc/engine/nodewiring.cpp
    @@ -46,7 +46,8 @@ namespace engine {
       {
         UNIMPLEMENTED ("build the actual wiring descriptor based on given operation options");
         
    -    return selectConfig(cache, process, inplace).fabricate();
    +//    Bits config (FlagInfo::CODE);
    +//    return selector(config);
       }
       // BlockAlloc > > >::fabricate();
     
    diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp
    index 2d5af1a3a..74fa86254 100644
    --- a/src/proc/engine/procnode.hpp
    +++ b/src/proc/engine/procnode.hpp
    @@ -60,6 +60,12 @@ namespace engine {
       
       typedef ProcNode* PNode;
       
    +  template
    +  struct RefArray
    +    {
    +      virtual E const& operator[] (uint i)  const =0;
    +      virtual ~RefArray() {}
    +    };
       
       /**
        * Interface: Description of the input and output ports,
    @@ -73,6 +79,13 @@ namespace engine {
           virtual uint getNrI()  const =0;           ///////////TODO: indeed need a virtual function??
           virtual uint getNrO()  const =0;
           
    +      RefArray& out;
    +      RefArray&  in;
    +      
    +      typedef void (ProcFunc) (BuffHandle::PBuff, uint);
    +      
    +      ProcFunc* processFunction;
    +      
         protected:
           /** the wiring-dependent part of the node operation.
            *  Includes the creation of a one-way state object on the stack
    
    From e5d75eee9bfc3456b563f66c43ba6fb4cdc72f83 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Mon, 21 Jul 2008 03:25:06 +0200
    Subject: [PATCH 12/40] test covering the stack-wise buffer pointer table
     allocation
    
    ---
     src/proc/engine/procnode.hpp                  |   7 +
     tests/56engine.tests                          |   5 +
     tests/SConscript                              |   8 +-
     .../components/proc/engine/bufftabletest.cpp  | 180 ++++++++++++++++++
     4 files changed, 196 insertions(+), 4 deletions(-)
     create mode 100644 tests/components/proc/engine/bufftabletest.cpp
    
    diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp
    index 74fa86254..13d6e2241 100644
    --- a/src/proc/engine/procnode.hpp
    +++ b/src/proc/engine/procnode.hpp
    @@ -87,6 +87,13 @@ namespace engine {
           ProcFunc* processFunction;
           
         protected:
    +      WiringDescriptor (RefArray& o, 
    +                        RefArray& i,
    +                        ProcFunc pFunc)
    +        : out(o), in(i),
    +          processFunction(pFunc)
    +        { }  
    +      
           /** the wiring-dependent part of the node operation.
            *  Includes the creation of a one-way state object on the stack
            *  holding the actual buffer pointers and issuing the recrusive pull() calls
    diff --git a/tests/56engine.tests b/tests/56engine.tests
    index 239fbca71..f2059ab32 100644
    --- a/tests/56engine.tests
    +++ b/tests/56engine.tests
    @@ -2,5 +2,10 @@ TESTING "Component Test Suite: Render Engine parts" ./test-components --group=en
     
     
     
    +TEST "BuffTable_test" BuffTable_test <
    + 
    +  This program is free software; you can redistribute it and/or
    +  modify it under the terms of the GNU General Public License as
    +  published by the Free Software Foundation; either version 2 of the
    +  License, or (at your option) any later version.
    + 
    +  This program is distributed in the hope that it will be useful,
    +  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +  GNU General Public License for more details.
    + 
    +  You should have received a copy of the GNU General Public License
    +  along with this program; if not, write to the Free Software
    +  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + 
    +* *****************************************************/
    +
    +
    +#include "common/test/run.hpp"
    +#include "common/error.hpp"
    +
    +#include "proc/engine/procnode.hpp"
    +#include "proc/engine/bufftable.hpp"
    +
    +#include 
    +#include 
    +#include 
    +#include 
    +
    +using test::Test;
    +using std::cout;
    +using std::rand;
    +
    +
    +namespace engine {
    +  namespace test {
    +  
    +    namespace { // used internally
    +      
    +      const uint TABLE_SIZ = 100000;
    +      const uint CHUNK_MAX = 8000;
    +      const uint WIDTH_MAX = 3;
    +      
    +      
    +      /** just some crap to pass in as ctor argument... */
    +      template
    +      struct DummyArray : RefArray
    +        {
    +          E decoy;
    +          E const& operator[] (uint i)  const  { return decoy;}
    +        };
    +      DummyArray dummy1;
    +      DummyArray  dummy2;
    +      
    +      
    +      /** a "hijacked" WiringDescriptor requesting
    +       *  a random number of inputs and outputs */
    +      struct MockSizeRequest
    +        : WiringDescriptor
    +        {
    +          uint ii,oo;
    +          
    +          MockSizeRequest()
    +            : WiringDescriptor(dummy1,dummy2,0),
    +              ii(rand() % CHUNK_MAX),
    +              oo(rand() % CHUNK_MAX)
    +            { }
    +          
    +          virtual uint getNrI()  const { return ii; }
    +          virtual uint getNrO()  const { return oo; }
    +          
    +          virtual BuffHandle callDown (State&, uint) const
    +          { throw lumiera::Error("not intended to be called"); }
    +        };
    +      
    +      
    +      
    +      void*
    +      detect_start_level (BuffTableStorage& sto)
    +      {
    +        return BuffTableChunk(MockSizeRequest(), sto ).outHandle;
    +      }       // address of first available storage element
    +      
    +      
    +      inline void*
    +      first_behind (BuffTable const& thisChunk, const uint nrI)
    +      {
    +        return &thisChunk.inHandle[nrI];
    +      }
    +      
    +      
    +      inline bool
    +      not_within(void* candidate, void* lower, void* upper)
    +      {
    +        return (candidate < lower) || (upper <= candidate);
    +      }
    +      
    +      
    +      bool
    +      consistencyCheck (BuffTable const& b, WiringDescriptor const& num, void* lastLevel)
    +      {
    +        return (b.outHandle == lastLevel )  // storage is allocated continously
    +            && (b.outBuff   <= b.inBuff  )  // input slots are behind the output slots
    +            && (b.outHandle <= b.inHandle)
    +            && (b.inBuff   == &b.outBuff  [num.getNrO()])
    +            && (b.inHandle == &b.outHandle[num.getNrO()])
    +            && (not_within(b.outBuff, b.outHandle, &b.inHandle[num.getNrO()])) // storage doesn't overlap
    +            && (not_within(b.inBuff,  b.outHandle, &b.inHandle[num.getNrO()]))
    +            && (not_within(b.outHandle, b.outBuff, &b.inBuff[num.getNrO()]))
    +            && (not_within(b.inHandle,  b.outBuff, &b.inBuff[num.getNrO()]))
    +             ;
    +      }
    +    } // (End) internal defs
    +    
    +    
    +    
    +    /*******************************************************************
    +     * @test create a random pattern of recursive invocations, each
    +     *       allocating a chunk out of a global buffer table storage.
    +     *       After returning, each allocation should be cleanly
    +     *       deallocated and the internal level in the storage vector
    +     *       should have dopped to zero again.
    +     */
    +    class BuffTable_test : public Test
    +      {
    +        typedef boost::scoped_ptr PSto;
    +        
    +        PSto pStorage;
    +        ulong counter;
    +        
    +        virtual void run(Arg arg) 
    +          {
    +             counter = 0;
    +             std::srand (time (NULL));
    +             
    +             // allocate storage block to be used chunk wise
    +             pStorage.reset (new BuffTableStorage (TABLE_SIZ));
    +             
    +             invocation (0, detect_start_level(*pStorage));
    +             
    +             pStorage.reset(0);  // dtor throws assertion error if corrupted
    +             
    +             cout << "BuffTable chunks allocated: "<
    Date: Tue, 22 Jul 2008 08:19:03 +0200
    Subject: [PATCH 13/40] more testing
    
    ---
     src/common/typelist.hpp              |   6 +-
     src/proc/engine/nodewiringconfig.hpp | 214 +++++++++++----------
     src/tool/try.cpp                     | 277 +++++++--------------------
     tests/common/typelistmaniptest.cpp   | 189 ++++++++++++++++++
     4 files changed, 382 insertions(+), 304 deletions(-)
     create mode 100644 tests/common/typelistmaniptest.cpp
    
    diff --git a/src/common/typelist.hpp b/src/common/typelist.hpp
    index f358a605e..f4921791c 100644
    --- a/src/common/typelist.hpp
    +++ b/src/common/typelist.hpp
    @@ -64,11 +64,15 @@ namespace lumiera
       namespace typelist
         {
         
    -    class NullType { };
    +    class NullType 
    +      { 
    +        typedef NullType List;
    +      };
         
         template 
         struct Node
           {
    +        typedef Node List;
             typedef H Head;
             typedef T Tail;
           };
    diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp
    index 1fc221bf6..24fd44ad9 100644
    --- a/src/proc/engine/nodewiringconfig.hpp
    +++ b/src/proc/engine/nodewiringconfig.hpp
    @@ -40,132 +40,151 @@ namespace lumiera {
       namespace typelist {
       
         template
    -    struct CondNode
    -      : TAIL
    -      { 
    -        typedef TAIL Next; 
    -      };
    -    template
    -    struct CondNode
    -      : Node
    -      { 
    -        typedef Node Next; 
    -      };
    +    struct CondNode                        { typedef TAIL  Next; };
         
    -    template class _P_>
    +    template
    +    struct CondNode         { typedef Node  Next; };
    +    
    +    
    +    template< class TYPES
    +            , template class _P_
    +            >
         struct Filter;
         
         template class _P_>
    -    struct Filter
    -      : NullType
    -      {
    -        typedef NullType Next;
    -      };
    -    template class _P_>
    -    struct Filter,_P_>
    -      : CondNode<_P_::value, TY, TYPES>
    -      {
    -        typedef CondNode<_P_::value, TY, TYPES> Next;
    -      };
    +    struct Filter            { typedef NullType  List; };
    +    
    +    template< class TY, class TYPES
    +            , template class _P_
    +            >
    +    struct Filter,_P_>      { typedef typename CondNode<_P_::value
    +                                                                      , TY
    +                                                                      , typename Filter::List
    +                                                                      >::Next      
    +                                                                      List; };
    +    
         
         
         template
    -    struct Append : Types::List
    -      { };
    +    struct Append                          { typedef Node::List>  List; };
    +    
    +    template< class TY, class TYPES
    +            , class TAIL
    +            >
    +    struct Append, TAIL>    { typedef Node::List>  List; };
    +    
    +    template
    +    struct Append >   { typedef Node   List; };
    +    
    +    template
    +    struct Append, NullType>    { typedef Node   List; };
    +    
         template
    -    struct Append
    -      : Node
    -      { };
    +    struct Append            { typedef Node   List; };
    +    
         template
    -    struct Append
    -      : Node
    -      { };
    -    template
    -    struct Append >
    -      : Node
    -      { };
    -    template
    -    struct Append, NullType>
    -      : Node
    -      { };
    -    template
    -    struct Append, TAIL>
    -      : Node >
    -      { };
    +    struct Append            { typedef Node   List; };
    +    
    +    template<>
    +    struct Append       { typedef NullType             List; };
    +    
    +    
    +    
         
         template
    -    struct PrefixAll
    -      : Append
    -      { };
    +    struct PrefixAll                       { typedef typename Append::List  List; };
    +    
         template
    -    struct PrefixAll
    -      : NullType
    -      { };
    -    template
    -    struct PrefixAll >
    -      : Node< Append
    -            , PrefixAll
    +    struct PrefixAll          { typedef NullType  List; };
    +    
    +    template< class T
    +            , class TY, class TYPES
                 >
    -      { };
    +    struct PrefixAll >   { typedef Node< typename Append::List
    +                                                         , typename PrefixAll::List
    +                                                         >     List; };
    +    
    +    
    +    
         
         template
    -    struct Distribute : Append
    -      { };
    -    template
    -    struct Distribute, TAIL>
    -      : Append< PrefixAll
    -              , Distribute
    -              >
    -      { };
    -    template class _PERM_>
    -    struct Combine : Distribute<_PERM_, NullType>
    -      { };
    -    template class _PERM_>
    -    struct Combine,_PERM_>
    -      : Distribute<_PERM_, Combine >
    -      { };
    +    struct Distribute                      { typedef typename Append::List  List; };
    +    
    +    template< class TY, class TYPES
    +            , class TAIL
    +            >
    +    struct Distribute,TAIL> { typedef typename Append< typename PrefixAll::List
    +                                                                    , typename Distribute::List
    +                                                                    >::List                    
    +                                                                    List; };
    +    
    +    
    +    
    +    
    +    template< class X
    +            , template class _PERMU_>
    +    struct Combine                         { typedef typename Distribute< typename _PERMU_::List
    +                                                                        , NullType
    +                                                                        >::List  List; };
    +    
    +    template< class TY, class TYPES
    +            , template class _PERMU_>
    +    struct Combine,_PERMU_> { typedef typename Distribute< typename _PERMU_::List
    +                                                                        , typename Combine::List
    +                                                                        >::List  List; };
    +    
         
         template
    -    struct FlagOnOff : Types::List
    -      { };
    +    struct FlagOnOff
    +      { 
    +        typedef typename Types::List  List;
    +      };
         
         template
         struct CombineFlags
    -      : Combine
    -      { };
    +      { 
    +        typedef typename Combine::List  List; 
    +      };
    +    
         
         
         using std::max;
         
         
    -    template  struct Flag                { };
    -    template<>          struct Flag<0> : NullType  { };
    +    template struct Flag    { typedef Flag     ID; };
    +    template<>         struct Flag<0> { typedef NullType ID; };
    +    
    +    
         template< char f1=0
                 , char f2=0
                 , char f3=0
                 , char f4=0
                 , char f5=0
                 >
    -    struct FlagTuple 
    -      : Types< Flag
    -             , Flag
    -             , Flag
    -             , Flag
    -             , Flag
    -             >::List
    -      { };
    -      
    -    template< char f1=0
    -            , char f2=0
    -            , char f3=0
    -            , char f4=0
    -            , char f5=0
    -            >
    -    struct Config
    -      {
    -        typedef FlagTuple Flags;
    +    struct Flags        
    +      { 
    +        typedef typename Types< typename Flag::ID
    +                              , typename Flag::ID
    +                              , typename Flag::ID
    +                              , typename Flag::ID
    +                              , typename Flag::ID
    +                              >::List       
    +                              Tuple;
           };
         
    +    
    +    template< char f1=0
    +            , char f2=0
    +            , char f3=0
    +            , char f4=0
    +            , char f5=0
    +            >
    +    struct Config 
    +      { 
    +        typedef typename Flags::Tuple  Flags; 
    +      };
    +    
    +    
         template
         struct DefineConfigByFlags;
         
    @@ -175,9 +194,10 @@ namespace lumiera {
                 , char f4
                 , char f5
                 >
    -    struct DefineConfigByFlags< FlagTuple >
    -      : Config
    -      { };
    +    struct DefineConfigByFlags< typename Flags::Tuple >
    +      { 
    +        typedef Config Config; 
    +      };
         
         
         /** 
    @@ -364,7 +384,7 @@ namespace engine {
               void
               visit (ulong code)
                 {
    -              typedef DefineConfigByFlags Config;
    +              typedef typename DefineConfigByFlags::Config Config;
                   factories_[code].reset (new FactoryHolder > (ctor_param_));
                 }
               
    diff --git a/src/tool/try.cpp b/src/tool/try.cpp
    index 85a3286a0..f4c56b8c4 100644
    --- a/src/tool/try.cpp
    +++ b/src/tool/try.cpp
    @@ -10,162 +10,88 @@
     // 4/08  - comparison operators on shared_ptr
     // 4/08  - conversions on the value_type used for boost::any
     // 5/08  - how to guard a downcasting access, so it is compiled in only if the involved types are convertible
    +// 7/08  - combining partial specialisation and subclasses 
     
     
     #include 
     #include 
     #include 
    -
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -#include 
    -
    +#include 
     
     using std::string;
     using std::cout;
     using std::ostream;
    +using boost::format;
     
     
    -    using boost::remove_pointer;
    -    using boost::remove_reference;
    -    using boost::is_convertible;
    -    using boost::is_polymorphic;
    -    using boost::is_base_of;
    -    using boost::enable_if;
    -    
    -    template 
    -    struct can_cast : boost::false_type {};
    -
    -    template 
    -    struct can_cast          { enum { value = is_base_of::value };};
    -
    -    template 
    -    struct can_cast         { enum { value = is_base_of::value };};
    -
    -    template 
    -    struct can_cast          { enum { value = is_base_of::value };};
    -    
    -    
    -    template 
    -    struct has_RTTI
    -      {
    -        typedef typename remove_pointer<
    -                typename remove_reference::type>::type TPlain;
    +#include 
    +using boost::enable_if;
    +#include 
    +using boost::is_base_of;
    +  
    +  
    +#include "common/typelistutil.hpp"
    +using lumiera::typelist::NullType;
    +using lumiera::typelist::Node;
    +using lumiera::typelist::Types;
    +  
    +  
    +  namespace {
           
    -        enum { value = is_polymorphic::value };
    -      };
    -    
    -    template 
    -    struct use_dynamic_downcast
    -      {
    -        enum { value = can_cast::value
    -                       &&  has_RTTI::value
    -                       &&  has_RTTI::value
    -             };
    -      };
    -    
    -    template 
    -    struct use_static_downcast
    -      {
    -        enum { value = can_cast::value
    -                    && (  !has_RTTI::value
    -                       || !has_RTTI::value
    -                       )
    -             };
    -      };
    -    
    -    template 
    -    struct use_conversion
    -      {
    -        enum { value = is_convertible::value
    -                    && !( use_static_downcast::value
    -                        ||use_dynamic_downcast::value
    -                        )
    -             };
    -      };
    -    
    +      boost::format fmt ("<%2i>");
    +      
    +      /** constant-wrapper type for debugging purposes,
    +       *  usable for generating lists of distinghishable types
    +       */
    +      template
    +      struct Num
    +        {
    +          enum{ VAL=I };
    +          static string str () { return boost::str (fmt % I); }
    +          Num()
    +            {
    +              cout << Num::str();
    +            }
    +        };
    +      
    +      
    +      template
    +        < class TYPES 
    +        , class BASE = NullType
    +        >
    +      struct Zoing;
    +      
    +      
    +      template
    +      struct Zoing
    +        : BASE
    +        { 
    +        };
    +      
    +      
    +      template
    +        < class TY, typename TYPES
    +        , class BASE
    +        >
    +      struct Zoing, BASE> 
    +        :  TY,
    +           Zoing
    +        { 
    +        };
    +      
    +      
    +  }
    +  
    +  typedef Types< Num<1>
    +               , Num<2>
    +               , Num<3>
    +               >::List List1;
    +  
    +  struct B
    +    { typedef List1 Zeug; };
    +  struct D : B 
    +    {};
     
    -    template
    -    struct EmptyVal
    -      {
    -        static X create() 
    -          {
    -            cout << " NULL() " << __PRETTY_FUNCTION__ <<"\n";
    -            return X(); 
    -          }
    -      };
    -    template
    -    struct EmptyVal
    -      {
    -        static X*& create() 
    -          {
    -            cout << " NULL & " << __PRETTY_FUNCTION__ <<"\n";
    -            static X* null(0);
    -            return null; 
    -          }
    -      };
    -    
    -    
    -    template
    -    struct NullAccessor
    -      {
    -        typedef RET Ret;
    -        
    -        static RET access  (...) { return ifEmpty(); }
    -        static RET ifEmpty ()    { return EmptyVal::create(); }
    -      };
    -    
    -    template
    -    struct AccessCasted : NullAccessor
    -      {
    -        using NullAccessor::access;
    -        
    -        template
    -        static  typename enable_if< use_dynamic_downcast, TAR>::type
    -        access (ELM& elem) 
    -          { 
    -            cout << " dynamic " << __PRETTY_FUNCTION__ <<"\n";
    -            return dynamic_cast (elem); 
    -          }
    -        
    -        template
    -        static  typename enable_if< use_static_downcast, TAR>::type
    -        access (ELM& elem) 
    -          { 
    -            cout << " static " << __PRETTY_FUNCTION__ <<"\n";
    -            return static_cast (elem); 
    -          }
    -        
    -        template
    -        static  typename enable_if< use_conversion, TAR>::type
    -        access (ELM& elem) 
    -          { 
    -            cout << " convert " << __PRETTY_FUNCTION__ <<"\n";
    -            return elem; 
    -          }
    -            
    -      };
    -
    -
    -
    -
    -struct B {};
    -struct D : B {};
    -
    -struct E : D
    -  {
    -    virtual ~E() {};
    -  };
    -struct F : E {};
    -
    -
    -ostream& operator<< (ostream& s, const B& b) { return s << "B{} adr="<<&b; }
    -ostream& operator<< (ostream& s, const D& d) { return s << "D{} adr="<<&d; }
    -ostream& operator<< (ostream& s, const E& e) { return s << "E{} adr="<<&e; }
    -ostream& operator<< (ostream& s, const F& f) { return s << "F{} adr="<<&f; }
     
     int 
     main (int argc, char* argv[])
    @@ -173,71 +99,10 @@ main (int argc, char* argv[])
         
         NOBUG_INIT;
         
    -    D d;
    -    D* pD =&d;
    -    B* pB =pD;
    -    D& rD = *pD;
    -    B& rB = *pB;
    -
    -    D*& rpD = pD;
    -    B*& rpB = pB;
    -
    -    F f;
    -    E& rE = f;
    -    E* pE = &f;
    -    D* pDE = pE;
    +    typedef Zoing Boing;
         
    -    cout <<  "is_base_of = "   << is_base_of::value << "\n";
    -    cout <<  "is_base_of = " << is_base_of::value << "\n";
    -    cout <<  "is_base_of = " << is_base_of::value << "\n";
    -
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -    cout <<  "can_cast = " << can_cast::value << "\n";
    -
    -    cout <<  "has_RTTI = " << has_RTTI::value << "\n";
    -    cout <<  "has_RTTI = " << has_RTTI::value << "\n";
    -    cout <<  "has_RTTI = " << has_RTTI::value << "\n";
    +    Boing();
         
    -    
    -    cout <<  "use_dynamic_downcast = " << use_dynamic_downcast::value << "\n";
    -    cout <<  "use_static_downcast = " << use_static_downcast::value << "\n";
    -    cout <<  "use_conversion = "      << use_conversion::value << "\n";
    -
    -    cout <<  "use_static_downcast = " << use_static_downcast::value << "\n";
    -    cout <<  "use_static_downcast = " << use_static_downcast::value << "\n";
    -    cout <<  "use_dynamic_downcast = " << use_dynamic_downcast::value << "\n";
    -    
    -    
    -    cout <<  "Access(D  as D&) --->" << AccessCasted::access(d)  << "\n";
    -    cout <<  "Access(D& as D&) --->" << AccessCasted::access(rD) << "\n";
    -    cout <<  "Access(B& as D&) --->" << AccessCasted::access(rB) << "\n";
    -    cout <<  "Access(D* as D*) --->" << AccessCasted::access(pD) << "\n";
    -    cout <<  "Access(B* as D*) --->" << AccessCasted::access(pB) << "\n";
    -    cout <<  "Access(D*& as D*&) --->" << AccessCasted::access(rpD) << "\n";
    -    cout <<  "Access(B*& as D*&) --->" << AccessCasted::access(rpB) << "\n";
    -    
    -    cout <<  "Access(D  as B&) --->" << AccessCasted::access(d)  << "\n";
    -    cout <<  "Access(D& as B&) --->" << AccessCasted::access(rD) << "\n";
    -    cout <<  "Access(B& as B&) --->" << AccessCasted::access(rB) << "\n";
    -    cout <<  "Access(D* as B*) --->" << AccessCasted::access(pD) << "\n";
    -    cout <<  "Access(B* as B*) --->" << AccessCasted::access(pB) << "\n";
    -    cout <<  "Access(D*& as B*&) --->" << AccessCasted::access(rpD) << "\n";
    -    cout <<  "Access(B*& as B*&) --->" << AccessCasted::access(rpB) << "\n";
    -    
    -    cout <<  "Access(D  as E&) --->" << AccessCasted::access(d) << "\n";
    -    cout <<  "Access(E& as F&) --->" << AccessCasted::access(rE) << "\n";
    -    cout <<  "Access(D(E)* as E*) --->" << AccessCasted::access(pDE) << "\n";
    -    cout <<  "Access(D(E)* as F*) --->" << AccessCasted::access(pDE) << "\n";
    -    cout <<  "Access(E* as F*) --->" << AccessCasted::access(pE) << "\n";
    -
         cout <<  "\ngulp\n";
         
         
    diff --git a/tests/common/typelistmaniptest.cpp b/tests/common/typelistmaniptest.cpp
    new file mode 100644
    index 000000000..0ae0d08ff
    --- /dev/null
    +++ b/tests/common/typelistmaniptest.cpp
    @@ -0,0 +1,189 @@
    +/*
    +  TypeListManip(Test)  -  appending, mixing and filtering typelists
    + 
    +  Copyright (C)         Lumiera.org
    +    2008,               Hermann Vosseler 
    + 
    +  This program is free software; you can redistribute it and/or
    +  modify it under the terms of the GNU General Public License as
    +  published by the Free Software Foundation; either version 2 of the
    +  License, or (at your option) any later version.
    + 
    +  This program is distributed in the hope that it will be useful,
    +  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +  GNU General Public License for more details.
    + 
    +  You should have received a copy of the GNU General Public License
    +  along with this program; if not, write to the Free Software
    +  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + 
    +* *****************************************************/
    +
    +
    +/** @file typelistmaniptest.cpp
    + ** \par checking the correct working of basic list manipulation
    + **
    + ** The semi-automatic assembly of processing node invocation code utilizes some
    + ** metaprogramming magic built upon simple list manipulation. As template metaprogramming
    + ** is kind-of functional programming, most of this is done by recursion.
    + ** To check the correct working, this test uses some constant-wrapper types and a debugging
    + ** template which will print these constant numbers when creating an instance. This allows to
    + ** verify in the output if lists of such constant-wrapper types were manipulated as expected.
    + **
    + ** @see typelisttest.cpp
    + ** @see typelistutil.hpp
    + ** @see nodewiringconfig.hpp real world usage example
    + **
    + */
    +
    +
    +#include "common/test/run.hpp"
    +#include "common/typelistutil.hpp"
    +#include "common/util.hpp"
    +#include "proc/engine/nodewiringconfig.hpp"  ///////////TODO move to typelistutil.hpp
    +
    +#include 
    +#include 
    +
    +using ::test::Test;
    +using std::string;
    +using std::cout;
    +
    +
    +namespace lumiera {
    +  namespace typelist {
    +    namespace test {
    +      
    +      
    +      namespace { // internal definitions
    +      
    +        boost::format fmt ("<%i>");
    +        
    +        /** constant-wrapper type for debugging purposes,
    +         *  usable for generating lists of distinghishable types
    +         */
    +        template
    +        struct Num
    +          {
    +            enum{ VAL=I };
    +            static string str () { return boost::str (fmt % I); }
    +          };
    +        
    +        
    +        /** debugging template, 
    +         *  printing the "number" used for intantiation on ctor call
    +         */
    +        template
    +        struct Printer;
    +        
    +        template
    +        struct Printer, BASE>
    +          : BASE
    +          {
    +            static string print () { return string("-") + Num::str() + BASE::print(); }
    +          };
    +        template
    +        struct Printer
    +          {
    +            static string print () { return "-"; }
    +          };
    +        
    +        
    +        
    +        typedef Types< Num<1>
    +                     , Num<2>
    +                     , Num<3>
    +                     >::List List1;
    +        typedef Types< Num<5>
    +                     , Num<6>
    +                     , Num<7>
    +                     >::List List2;
    +        
    +        
    +#define DIAGNOSE(LIST) \
    +        typedef InstantiateChained >  Contents_##LIST;
    +                     
    +#define DISPLAY(NAME)  \
    +        DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n";
    +        
    +      } // (End) internal defs
    +      
    +      
    +      
    +      
    +      
    +      
    +      /*************************************************************************
    +       * @test check utilities for manipulating lists-of-types.
    +       *       
    • build an list of constant-wrapper-types and + * print them for debugging purpose.
    • + *
    + */ + class TypeListManipl_test : public Test + { + virtual void run(Arg arg) + { + check_diagnostics (); + check_append (); + } + + + void check_diagnostics () + { + // Explanation: the DISPLAY macro expands as follows.... + typedef InstantiateChained > Contents_List1; + cout << "List1" << "\t" << Contents_List1::print() << "\n"; + + // That is: we instantiate the "Printer" template for each of the types in List1, + // forming an inheritance chain. I.e. the defined Type "Contents_List1" inherits + // from each instantiation (single inheritance). + // The print() function is defined to create a string showing each. + + DISPLAY (List2); + } + + + void check_append () + { + typedef Append Append1; + DISPLAY (Append1); + + typedef Append,Num<22> > Append2; + DISPLAY (Append2); + + typedef Append,NullType> Append3; + DISPLAY (Append3); + + typedef Append > Append4; + DISPLAY (Append4); + + typedef Append Append5; + DISPLAY (Append5); + + typedef Append Append6; + DISPLAY (Append6); + + typedef Append,List2> Append7; + DISPLAY (Append7); + + typedef Append > Append8; + DISPLAY (Append8); + + typedef Append Append9; + DISPLAY (Append9); + } + + }; + + + /** Register this test class... */ + LAUNCHER (TypeListManipl_test, "unit common"); + + + + } // namespace test + + } // namespace typelist + +} // namespace lumiera From 06cc4c93fdccb6ba1e1cce7276f2aaf2c2a3f159 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 24 Jul 2008 06:38:21 +0200 Subject: [PATCH 14/40] test the combination generator --- src/proc/engine/nodewiringconfig.hpp | 14 +-- tests/common/typelistmaniptest.cpp | 122 +++++++++++++++++++++++++-- 2 files changed, 121 insertions(+), 15 deletions(-) diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index 24fd44ad9..a385ce2a2 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -46,9 +46,7 @@ namespace lumiera { struct CondNode { typedef Node Next; }; - template< class TYPES - , template class _P_ - > + template< class TYPES , template class _P_> struct Filter; template class _P_> @@ -57,7 +55,7 @@ namespace lumiera { template< class TY, class TYPES , template class _P_ > - struct Filter,_P_> { typedef typename CondNode<_P_::value + struct Filter,_P_> { typedef typename CondNode< _P_::value , TY , typename Filter::List >::Next @@ -92,7 +90,7 @@ namespace lumiera { template - struct PrefixAll { typedef typename Append::List List; }; + struct PrefixAll { typedef Node< typename Append::List, NullType> List; }; template struct PrefixAll { typedef NullType List; }; @@ -108,7 +106,10 @@ namespace lumiera { template - struct Distribute { typedef typename Append::List List; }; + struct Distribute { typedef typename PrefixAll::List List; }; + + template + struct Distribute,NullType> { typedef Node List; }; template< class TY, class TYPES , class TAIL @@ -148,7 +149,6 @@ namespace lumiera { - using std::max; template struct Flag { typedef Flag ID; }; diff --git a/tests/common/typelistmaniptest.cpp b/tests/common/typelistmaniptest.cpp index 0ae0d08ff..9420aa154 100644 --- a/tests/common/typelistmaniptest.cpp +++ b/tests/common/typelistmaniptest.cpp @@ -61,7 +61,7 @@ namespace lumiera { boost::format fmt ("<%i>"); /** constant-wrapper type for debugging purposes, - * usable for generating lists of distinghishable types + * usable for generating lists of distinguishable types */ template struct Num @@ -77,16 +77,44 @@ namespace lumiera { template struct Printer; + template + struct Printer + { + static string print () { return "-"; } + }; + template struct Printer, BASE> : BASE { static string print () { return string("-") + Num::str() + BASE::print(); } }; - template - struct Printer + + + typedef Printer NullP; + + /** call the debug-print for a typelist + * utilizing the Printer template */ + template + string + printSublist () + { + typedef InstantiateChained SubList; + return SubList::print(); + } + + /** Spezialisation for debug-printing of a nested sublist */ + template + struct Printer, BASE> + : BASE { - static string print () { return "-"; } + static string print () + { + //typedef InstantiateChained, Printer, NullP> SubList; + typedef Node List; + return string("\n\t+--") + printSublist()+"+" + + BASE::print(); + } }; @@ -99,10 +127,16 @@ namespace lumiera { , Num<6> , Num<7> >::List List2; + + + template struct CountDown { typedef NullType List; }; + template<> struct CountDown > { typedef Node, NullType> List; }; + template struct CountDown > { typedef Node, typename CountDown >::List> List; }; + #define DIAGNOSE(LIST) \ - typedef InstantiateChained > Contents_##LIST; + typedef InstantiateChained Contents_##LIST; #define DISPLAY(NAME) \ DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n"; @@ -116,9 +150,13 @@ namespace lumiera { /************************************************************************* * @test check utilities for manipulating lists-of-types. - *
    • build an list of constant-wrapper-types and - * print them for debugging purpose.
    • - *
    + * - build an list of constant-wrapper-types and + * print them for debugging purpose. + * - append lists, single elements and NullType + * in various combinations + * - filtering out some types from a typelist by + * using a "predicate template" (metafuction) + * - building combinations and permutations */ class TypeListManipl_test : public Test { @@ -126,6 +164,10 @@ namespace lumiera { { check_diagnostics (); check_append (); + check_filter (); + check_prefix (); + check_distribute(); + check_combine(); } @@ -174,6 +216,70 @@ namespace lumiera { DISPLAY (Append9); } + + template struct IsEven { enum {value = false }; }; + template struct IsEven > { enum {value = (0 == I % 2) }; }; + + void check_filter () + { + typedef Filter::List, IsEven > FilterEven; + DISPLAY (FilterEven); + } + + + void check_prefix () + { + typedef PrefixAll,Num<22> > Prefix1; + DISPLAY (Prefix1); + + typedef PrefixAll,List1> Prefix2; + DISPLAY (Prefix2); + + typedef PrefixAll Prefix3; + DISPLAY (Prefix3); + + typedef Types,List2::List>::List List_of_Lists; + typedef PrefixAll,List_of_Lists> Prefix4; + DISPLAY (Prefix4); + + typedef PrefixAll Prefix5; + DISPLAY (Prefix5); + + typedef PrefixAll Prefix6; + DISPLAY (Prefix6); + } + + + void check_distribute() + { + typedef Distribute, List1> Dist1; + DISPLAY (Dist1); + + typedef Types,Num<22>,Num<33> >::List Prefixes; + typedef Distribute > Dist2; + DISPLAY (Dist2); + + typedef Distribute Dist3; + DISPLAY (Dist3); + + typedef Distribute::List> Dist4; + DISPLAY (Dist4); + } + + + void check_combine() + { + typedef CountDown > Down; + DISPLAY (Down); + + typedef Combine Combi; + DISPLAY (Combi); + + typedef CombineFlags OnOff; + DISPLAY (OnOff); + } + + }; From 0dec8de6a32881cf74c89f3e0efa758466a65d74 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 28 Jul 2008 04:33:10 +0200 Subject: [PATCH 15/40] test covering ennumeration of possible configs --- src/common/typelist.hpp | 2 + src/proc/engine/nodewiringconfig.hpp | 104 +++++- src/tool/try.cpp | 80 ++-- tests/common/typelist/configflagstest.cpp | 351 ++++++++++++++++++ .../{ => typelist}/typelistmaniptest.cpp | 19 +- tests/common/{ => typelist}/typelisttest.cpp | 0 .../{ => typelist}/typelistutiltest.cpp | 0 7 files changed, 502 insertions(+), 54 deletions(-) create mode 100644 tests/common/typelist/configflagstest.cpp rename tests/common/{ => typelist}/typelistmaniptest.cpp (93%) rename tests/common/{ => typelist}/typelisttest.cpp (100%) rename tests/common/{ => typelist}/typelistutiltest.cpp (100%) diff --git a/src/common/typelist.hpp b/src/common/typelist.hpp index f4921791c..2f45ae85b 100644 --- a/src/common/typelist.hpp +++ b/src/common/typelist.hpp @@ -117,6 +117,8 @@ namespace lumiera }; + typedef Node NodeNull; + } // namespace typelist diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index a385ce2a2..74e987325 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -38,7 +38,18 @@ #include "common/typelistutil.hpp" namespace lumiera { namespace typelist { - + + template class _TRANS_> + struct Apply { typedef TY List; }; + + template< class TY, class TYPES + , template class _TRANS_ + > + struct Apply, _TRANS_ > { typedef Node< typename _TRANS_::Type + , typename Apply::List + > List; }; + + template struct CondNode { typedef TAIL Next; }; @@ -95,6 +106,9 @@ namespace lumiera { template struct PrefixAll { typedef NullType List; }; + template + struct PrefixAll { typedef Node< typename Append::List, NullType> List; }; + template< class T , class TY, class TYPES > @@ -107,9 +121,9 @@ namespace lumiera { template struct Distribute { typedef typename PrefixAll::List List; }; - - template - struct Distribute,NullType> { typedef Node List; }; + + template + struct Distribute { typedef NullType List; }; template< class TY, class TYPES , class TAIL @@ -125,8 +139,10 @@ namespace lumiera { template< class X , template class _PERMU_> struct Combine { typedef typename Distribute< typename _PERMU_::List - , NullType + , Node >::List List; }; + template< template class _PERMU_> + struct Combine { typedef Node List; }; template< class TY, class TYPES , template class _PERMU_> @@ -138,7 +154,7 @@ namespace lumiera { template struct FlagOnOff { - typedef typename Types::List List; + typedef Node > List; }; template @@ -149,6 +165,46 @@ namespace lumiera { + /////////// meta utils + + typedef char Yes_t; + struct No_t { char padding[8]; }; + + + /** semi-automatic detection if an instantiation is possible. + * Requires help by the template to be tested, which needs to define + * a typedef member \c is_defined. The embedded metafunction Test can be used + * as a predicate for filtering types which may yield a valid instantiation + * of the candidate template in question. + * \par + * A fully automated solution for this problem is impossible by theoretic reasons. + * Any non trivial use of such a \c is_defined trait would break the "One Definition Rule", + * as the state of any type can change from "partially defined" to "fully defined" over + * the course of any translation unit. Thus, even if there may be a \em solution out there, + * we can expect it to break at some point by improvements/fixes to the C++ Language. + */ + template class _CandidateTemplate_> + struct Instantiation + { + + template + class Test + { + typedef _CandidateTemplate_ Instance; + + template + static Yes_t check(typename U::is_defined *); + template + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(0))); + }; + }; + + + + template struct Flag { typedef Flag ID; }; @@ -170,6 +226,7 @@ namespace lumiera { , typename Flag::ID >::List Tuple; + typedef Tuple List; }; @@ -181,22 +238,39 @@ namespace lumiera { > struct Config { - typedef typename Flags::Tuple Flags; + typedef typename Flags::Tuple Flags; + typedef Flags List; }; - template - struct DefineConfigByFlags; + template + struct ConfigSetFlag; - template< char f1 + template< char Fl + , char f1 , char f2 , char f3 , char f4 - , char f5 + , char IGN > - struct DefineConfigByFlags< typename Flags::Tuple > + struct ConfigSetFlag > + { + typedef Config Config; + }; + + template > + struct DefineConfigByFlags + { + typedef CONF Config; + typedef Config Type; + }; + template + struct DefineConfigByFlags< Node,FLAGS>, CONF> { - typedef Config Config; + typedef typename ConfigSetFlag< Fl + , typename DefineConfigByFlags::Config + >::Config Config; + typedef Config Type; }; @@ -244,7 +318,7 @@ namespace lumiera { > > { typedef Node, FLAGS> ThisConfig; - enum{ BITS = MAX (FlagInfo::BITS, FlagInfo::BITS) + enum{ BITS = MAX (char(FlagInfo::BITS), char(FlagInfo::BITS)) }; template @@ -259,7 +333,7 @@ namespace lumiera { - + } // namespace typelist } // namespace lumiera diff --git a/src/tool/try.cpp b/src/tool/try.cpp index f4c56b8c4..7d9d99000 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -28,6 +28,8 @@ using boost::format; using boost::enable_if; #include using boost::is_base_of; +#include +#include #include "common/typelistutil.hpp" @@ -55,53 +57,63 @@ using lumiera::typelist::Types; }; - template - < class TYPES - , class BASE = NullType - > - struct Zoing; - - - template - struct Zoing - : BASE - { - }; - - - template - < class TY, typename TYPES - , class BASE - > - struct Zoing, BASE> - : TY, - Zoing - { - }; - + + template class _CandidateTemplate_> + class Instantiation + { + template + struct If_possibleArgument : _CandidateTemplate_ + { + typedef void Type; + }; + + public: + + template + struct Test + : boost::false_type {}; + + template + struct Test::Type > + : boost::true_type {}; + + }; } - typedef Types< Num<1> - , Num<2> - , Num<3> - >::List List1; + struct Boing { typedef boost::true_type is_defined; }; - struct B - { typedef List1 Zeug; }; - struct D : B - {}; + template struct Zoing ; + + template<> struct Zoing<2> : Boing { enum{wau = 2}; }; + template<> struct Zoing<5> : Boing { enum{wau = 5}; }; + typedef char yes_type; + struct no_type + { + char padding[8]; + }; + template + yes_type check(typename U::is_defined *); + template + no_type check(...); + + int main (int argc, char* argv[]) { NOBUG_INIT; - typedef Zoing Boing; + typedef Zoing<2> Z2; + typedef Zoing<3> Z3; + typedef Zoing<5> Z5; + + cout << sizeof(check(0)) << " / " + << sizeof(check(0)) << " / " + << sizeof(check(0)) ; - Boing(); cout << "\ngulp\n"; diff --git a/tests/common/typelist/configflagstest.cpp b/tests/common/typelist/configflagstest.cpp new file mode 100644 index 000000000..78584f30c --- /dev/null +++ b/tests/common/typelist/configflagstest.cpp @@ -0,0 +1,351 @@ +/* + ConfigFlags(Test) - generating a configuration type defined by flags + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +/** @file configflagstest.cpp + ** \par build a type representing a single configuration defined by a set of flags + ** + ** The semi-automatic assembly of processing node invocation code utilizes some + ** metaprogramming to generate a factory, which in turn produces node wiring objects + ** according to the configuration to be used for the corresponding ProcNode. This relies on + ** generating a distinct type based on a given set of configuration flags, + ** which is covered by this test. + ** + ** @see configflags.hpp + ** @see typelistmanip.hpp + ** @see nodewiringconfig.hpp real world usage example + ** + */ + + +#include "common/test/run.hpp" +#include "common/typelistutil.hpp" +#include "common/util.hpp" +//#include "common/typelist/typelistmanip.hpp" +#include "proc/engine/nodewiringconfig.hpp" ///////////TODO move to configflags.hpp + +#include +#include + +using ::test::Test; +using std::string; +using std::cout; +using boost::format; + + +namespace lumiera { + namespace typelist { + namespace test { + + + namespace { // internal definitions + + enum Cases + { ONE = 1 + , TWO + , THR + , FOU + , NUM_Cases = FOU + + , NOT_SET = 0 + }; + + format fmt ("-<%u>%s"); + + struct NullP + { + static string print () { return "-"; } + }; + + /** debugging template, + * printing the "number" used for intantiation on ctor call + */ + template + struct Printer; + + template + struct Printer + : BASE + { + static string print () { return str( fmt % "·" % BASE::print()); } + }; + + template + struct Printer, BASE> + : BASE + { + static string print () { return str( fmt % uint(Fl) % BASE::print()); } + }; + + + + /** call the debug-print for a typelist + * utilizing the Printer template */ + template + string + printSublist () + { + typedef InstantiateChained SubList; + return SubList::print(); + } + + /** Spezialisation for debug-printing of a nested sublist */ + template + struct Printer, BASE> + : BASE + { + static string print () + { + typedef Node List; + return string("\n\t+--") + printSublist()+"+" + + BASE::print(); + } + }; + + template + struct Printer, BASE> + : BASE + { + static string print () + { + typedef typename Config::Flags FlagList; + return string("\n\t+-Conf-[") + printSublist()+"]" + + BASE::print(); + } + }; + + +#define DIAGNOSE(LIST) \ + typedef InstantiateChained Contents_##LIST; + +#define DISPLAY(NAME) \ + DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n"; + + + + /* === Test data === */ + typedef Config<> Conf0; + typedef Config Conf1; + typedef Config Conf2; + typedef Config Conf3; + typedef Config Conf4; + + typedef Flags::Tuple Flags1; + typedef Flags::Tuple Flags2; + typedef Types SomeFlagsets; + + typedef Flags::Tuple AllFlags; + typedef CombineFlags AllFlagCombinations; + + + + /** a type which is only partially defined, for some configs. + * In ConfigFlags_test::check_filter() we use the metaprogramming machinery + * to figure out all possible configs for which \c struct Maybe is defined. + * (for this to work, the "defined" specialisations need to provide a + * typedef \c is_defined ) + */ + template struct Maybe; + + struct Indeed { typedef Yes_t is_defined; }; + template<> struct Maybe : Indeed { enum{ CODE = 1 }; }; + template<> struct Maybe : Indeed { enum{ CODE = 3 }; }; + + template + struct Maybe > + { + typedef Yes_t is_defined; + + enum{ CODE = 20 + Fl }; + }; + + + } // (End) internal defs + + + + + + + /************************************************************************** + * @test check the handling of types representing a specific configuration. + * Basically, this is a bitset like feature, but working on types + * instead of runtime values. The Goal is to detect automatically + * all possible defined specialisations of some template based on + * such configuration-tuples. This allows us to assemble the glue code + * for pulling data from processing nodes out of small building blocks + * in all possible configuraions. + */ + class ConfigFlags_test : public Test + { + virtual void run(Arg arg) + { + check_testdata (); + check_flags(); + check_instantiation (); + check_filter (); + check_FlagInfo (); + } + + + void check_testdata () + { + DISPLAY (Conf0); + DISPLAY (Conf1); + DISPLAY (Conf2); + DISPLAY (Conf3); + DISPLAY (Conf4); + + DISPLAY (AllFlags); + } + + + /** @test conversion between list-of-flags and a config-type in both directions */ + void check_flags () + { + cout << "\n==== check_flags()\n"; + + typedef Config Flags1; + typedef Flags Flags2; + DISPLAY (Flags1); + DISPLAY (Flags2); + // should denote the same type + Flags1::Flags flags1 = Flags2::Tuple(); + Flags2::Tuple flags2 = flags1; + ASSERT (1==sizeof(flags1)); // pure marker-type without content + ASSERT (1==sizeof(flags2)); + + typedef DefineConfigByFlags,NullType> >::Config SimpleConfig_defined_by_Typelist; + DISPLAY (SimpleConfig_defined_by_Typelist); + + typedef DefineConfigByFlags::Config AnotherConfig_defined_by_Typelist; + DISPLAY (AnotherConfig_defined_by_Typelist); + } + + + /** @test creates a predicate template (metafunction) returning true + * iff the template \c Maybe is defined for the configuration in question + */ + void check_instantiation () + { + #define CAN_INSTANTIATE(NAME) \ + cout << "defined " \ + << STRINGIFY(NAME) \ + << "? ---> " \ + << Instantiation::Test::value << "\n"; + + cout << "\n==== check_instantiation()\n"; + CAN_INSTANTIATE (Conf0); + CAN_INSTANTIATE (Conf1); + CAN_INSTANTIATE (Conf2); + CAN_INSTANTIATE (Conf3); + CAN_INSTANTIATE (Conf4); + + typedef Config Trash; + CAN_INSTANTIATE (Trash); + } + + + /** @test given a list of flag-tuples, we first create config-types out of them + * and then filter out those configs for which \c template Maybe is defined + */ + void check_filter () + { + cout << "\n==== check_filter()\n"; + + DISPLAY (SomeFlagsets); + + typedef Apply Configs_defined_by_Flagsets; + DISPLAY (Configs_defined_by_Flagsets); + + typedef Filter::Test> Filter_possible_Configs; + DISPLAY (Filter_possible_Configs); + + + DISPLAY (AllFlagCombinations); + typedef Apply ListAllConfigs; + DISPLAY (ListAllConfigs); + + typedef Filter::Test> Filter_all_possible_Configs; + DISPLAY (Filter_all_possible_Configs); + } + + + + struct TestVisitor + { + string result; + TestVisitor() : result ("TestVisitor application:\n") {} + + /* === visitation interface === */ + typedef string Ret; + + Ret done() {return result; } + + template + void + visit (ulong code) + { + typedef typename DefineConfigByFlags::Config Config; + result += str (format ("visit(code=%u) --> %s\n") + % code % Printer::print() ); + } + }; + + /** @test FlagInfo metafunction, which takes as argument a list-of-flags + * as well as a list-of-lists-of-flags and especially allows to + * apply a visitor object to the latter + */ + void check_FlagInfo() + { + cout << "\n==== check_FlagInfo()\n"; + + DISPLAY (Flags1); + cout << "max bit : " << FlagInfo::BITS <<"\n"; + cout << "binary code: " << FlagInfo::CODE <<"\n"; + + DISPLAY (SomeFlagsets); + cout << "max bit in [SomeFlagsets] : " << FlagInfo::BITS <<"\n"; + + TestVisitor visitor; + cout << FlagInfo::accept (visitor); + } + + + void check_Factory() + { + } + + + }; + + + /** Register this test class... */ + LAUNCHER (ConfigFlags_test, "unit common"); + + + + } // namespace test + + } // namespace typelist + +} // namespace lumiera diff --git a/tests/common/typelistmaniptest.cpp b/tests/common/typelist/typelistmaniptest.cpp similarity index 93% rename from tests/common/typelistmaniptest.cpp rename to tests/common/typelist/typelistmaniptest.cpp index 9420aa154..7b285d205 100644 --- a/tests/common/typelistmaniptest.cpp +++ b/tests/common/typelist/typelistmaniptest.cpp @@ -22,14 +22,14 @@ /** @file typelistmaniptest.cpp - ** \par checking the correct working of basic list manipulation + ** \par checking the correct working of simple list manipulation metafunctions ** ** The semi-automatic assembly of processing node invocation code utilizes some ** metaprogramming magic built upon simple list manipulation. As template metaprogramming ** is kind-of functional programming, most of this is done by recursion. ** To check the correct working, this test uses some constant-wrapper types and a debugging - ** template which will print these constant numbers when creating an instance. This allows to - ** verify in the output if lists of such constant-wrapper types were manipulated as expected. + ** template which will print these constant numbers, thus allowing to verify in the output + ** if various lists of such constant-wrapper types were manipulated as expected. ** ** @see typelisttest.cpp ** @see typelistutil.hpp @@ -72,7 +72,7 @@ namespace lumiera { /** debugging template, - * printing the "number" used for intantiation on ctor call + * printing the "number" used for intantiation */ template struct Printer; @@ -110,7 +110,6 @@ namespace lumiera { { static string print () { - //typedef InstantiateChained, Printer, NullP> SubList; typedef Node List; return string("\n\t+--") + printSublist()+"+" + BASE::print(); @@ -163,6 +162,7 @@ namespace lumiera { virtual void run(Arg arg) { check_diagnostics (); + check_apply (); check_append (); check_filter (); check_prefix (); @@ -216,6 +216,15 @@ namespace lumiera { DISPLAY (Append9); } + template struct AddConst2 { typedef X Type; }; + template struct AddConst2 > { typedef Num Type; }; + + void check_apply () + { + typedef Apply Added2; + DISPLAY (Added2); + } + template struct IsEven { enum {value = false }; }; template struct IsEven > { enum {value = (0 == I % 2) }; }; diff --git a/tests/common/typelisttest.cpp b/tests/common/typelist/typelisttest.cpp similarity index 100% rename from tests/common/typelisttest.cpp rename to tests/common/typelist/typelisttest.cpp diff --git a/tests/common/typelistutiltest.cpp b/tests/common/typelist/typelistutiltest.cpp similarity index 100% rename from tests/common/typelistutiltest.cpp rename to tests/common/typelist/typelistutiltest.cpp From 276ac8e0826f25bc816d84ac82770b433c17afab Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 29 Jul 2008 18:03:37 +0200 Subject: [PATCH 16/40] test building a table of factories dealing with the possible configs --- src/proc/engine/nodewiringconfig.hpp | 60 +++++++++++---------- tests/common/typelist/configflagstest.cpp | 63 +++++++++++++++++++---- 2 files changed, 88 insertions(+), 35 deletions(-) diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index 74e987325..9223f5633 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -31,7 +31,9 @@ #include #include #include +#include #include +#include /////////////////TODO: library @@ -277,16 +279,13 @@ namespace lumiera { /** * Helper for calculating values and for * invoking runtime code based on a given FlagTuple. - * Can also be used on a Typelist of several FlagTuples. + * Can also be used on a Typelist of several Configs. * The latter case is typically used to invoke an operation * while ennumerating all Flag-Configurations defined in Code. * An example would be to build (at runtime) an dispatcher table. - * Explanation: the supposed data structure is a list of lists, - * where "list" here refers to a Typelist (compile time). Each - * of the inner lists in this structure represents a single - * Flag-Configuration. For the Case covering the outer List, - * we provide a templated visitaion function, which can accept - * a functor object to be invoked on each Flag-Configuration. + * Explanation: For the Case covering a List of Configs, we provide + * a templated visitaion function, which can accept a functor object + * to be invoked on each Configuration. */ template struct FlagInfo; @@ -312,20 +311,18 @@ namespace lumiera { return functor.done(); } }; - template - struct FlagInfo, FLAGS> - , TAIL - > > + template + struct FlagInfo > { - typedef Node, FLAGS> ThisConfig; - enum{ BITS = MAX (char(FlagInfo::BITS), char(FlagInfo::BITS)) + typedef typename CONF::Flags ThisFlags; + enum{ BITS = MAX (char(FlagInfo::BITS), char(FlagInfo::BITS)) }; template static typename FUNC::Ret accept (FUNC& functor) { - functor.template visit(FlagInfo::CODE); + functor.template visit(FlagInfo::CODE); return FlagInfo::accept (functor); } }; @@ -353,6 +350,8 @@ namespace engine { using lumiera::typelist::DefineConfigByFlags; ///////TODO + using boost::lexical_cast; + using util::contains; enum Cases { @@ -404,17 +403,23 @@ namespace engine { * bulk allocation memory block -- typically for each separate * segment of the Timeline and processing node graph. * - * NOw the selection of the possible flag configurations, for which + * Now the selection of the possible flag configurations, for which * Factory instances are created in the table, is governed by the * type parameter of the ConfigSelector ctor. This type parameter * needs to be a Typelist of Typelists, each representing a flag * configuration. The intention is to to drive this selection by * the use of template metaprogramming for extracting all * currently defined StateProxy object configurations. + * @todo as the facories live only within the enclosed table (map) + * we could allocate them in-place. Unfortunately this is + * non-trivial, because the stl containers employ + * value semantics and thus do a copy even on insert. + * Thus, for now we use a shared_ptr to hold the factory + * heap allocated. */ template< template class Factory - , class PAR ///< ctor parameter of the Factories - , class RET ///< common base class of the Factory's products + , typename PAR ///< ctor parameter of the Factories + , typename RET ///< common base class of the Factory's products > class ConfigSelector { @@ -434,8 +439,8 @@ namespace engine { }; - typedef boost::scoped_ptr PFunc; - typedef std::map ConfigTable; + typedef boost::shared_ptr PFunc; + typedef std::map ConfigTable; ConfigTable possibleConfig_; ///< Table of factories @@ -454,12 +459,12 @@ namespace engine { typedef void Ret; - template + template void - visit (ulong code) + visit (size_t code) { - typedef typename DefineConfigByFlags::Config Config; - factories_[code].reset (new FactoryHolder > (ctor_param_)); + PFunc pFactory (new FactoryHolder > (ctor_param_)); + factories_[code] = pFactory; } void done() {} @@ -467,7 +472,7 @@ namespace engine { public: template - ConfigSelector(PAR factory_ctor_param) + ConfigSelector(CONFS const&, PAR factory_ctor_param) { FactoryTableBuilder buildTable(this->possibleConfig_, factory_ctor_param ); @@ -477,9 +482,12 @@ namespace engine { } RET - operator() (Bits configFlags) ///< invoke the factory corresponding to the given config + operator() (size_t configFlags) ///< invoke the factory corresponding to the given config { - return possibleConfig_[configFlags]->invoke(); + if (contains (possibleConfig_, configFlags)) + return possibleConfig_[configFlags]->invoke(); + else + throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="+lexical_cast(configFlags)); } }; diff --git a/tests/common/typelist/configflagstest.cpp b/tests/common/typelist/configflagstest.cpp index 78584f30c..1c9dbdf83 100644 --- a/tests/common/typelist/configflagstest.cpp +++ b/tests/common/typelist/configflagstest.cpp @@ -167,8 +167,8 @@ namespace lumiera { template struct Maybe; struct Indeed { typedef Yes_t is_defined; }; - template<> struct Maybe : Indeed { enum{ CODE = 1 }; }; - template<> struct Maybe : Indeed { enum{ CODE = 3 }; }; + template<> struct Maybe : Indeed { enum{ CODE = 10 }; }; + template<> struct Maybe : Indeed { enum{ CODE = 30 }; }; template struct Maybe > @@ -204,6 +204,7 @@ namespace lumiera { check_instantiation (); check_filter (); check_FlagInfo (); + check_ConfigSelector (); } @@ -301,13 +302,12 @@ namespace lumiera { Ret done() {return result; } - template + template void visit (ulong code) { - typedef typename DefineConfigByFlags::Config Config; result += str (format ("visit(code=%u) --> %s\n") - % code % Printer::print() ); + % code % Printer::print() ); } }; @@ -323,16 +323,61 @@ namespace lumiera { cout << "max bit : " << FlagInfo::BITS <<"\n"; cout << "binary code: " << FlagInfo::CODE <<"\n"; - DISPLAY (SomeFlagsets); - cout << "max bit in [SomeFlagsets] : " << FlagInfo::BITS <<"\n"; + typedef Apply SomeConfigs; + DISPLAY (SomeConfigs); + cout << "max bit in [SomeConfigs] : " << FlagInfo::BITS <<"\n"; TestVisitor visitor; - cout << FlagInfo::accept (visitor); + cout << FlagInfo::accept (visitor); } - void check_Factory() + template + struct TestFactory + { + uint operator() () { return offset_ + Maybe::CODE; } + TestFactory(long o) : offset_(o) {} + + private: + long offset_; + }; + + + void check_ConfigSelector() { + cout << "\n==== check_ConfigSelector()\n"; + + typedef Apply ListAllConfigs; + typedef Filter::Test> Possible_Configs; + DISPLAY (Possible_Configs); + + typedef engine::ConfigSelector TestFactorySelector; + + const long offset = 1000; // parameter fed to all TestFactory ctors + TestFactorySelector testConfigSelector (Possible_Configs::List(), offset); + + + #define INVOKE_CONFIG_SELECTOR(CODE) \ + cout << " Flag-code = " << CODE \ + << " ConfigSelector() ---> " \ + << testConfigSelector (CODE) << "\n"; + + INVOKE_CONFIG_SELECTOR (2); + INVOKE_CONFIG_SELECTOR (12); + INVOKE_CONFIG_SELECTOR (20); + INVOKE_CONFIG_SELECTOR (4); + INVOKE_CONFIG_SELECTOR (8); + + try + { + INVOKE_CONFIG_SELECTOR (23); + NOTREACHED ; + } + catch (lumiera::error::Invalid& err) + { + cout << err.what() << "\n"; + lumiera_error (); // reset errorflag + } } From 847cc9c267b4a917d380b2d8f1c2165be2b2d1a3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 30 Jul 2008 03:56:13 +0200 Subject: [PATCH 17/40] WIP rearranged some headers --- src/common/configrules.hpp | 2 +- src/common/meta/configflags.hpp | 180 +++++++++ .../{typelistutil.hpp => meta/generator.hpp} | 46 +-- src/common/{ => meta}/typelist.hpp | 4 +- src/common/meta/typelistutil.hpp | 213 ++++++++++ src/common/meta/util.hpp | 74 ++++ src/common/variant.hpp | 2 +- src/common/visitor.hpp | 2 +- src/common/wrapperptr.hpp | 2 +- src/proc/engine/nodeoperation.hpp | 13 +- src/proc/engine/nodewiringconfig.hpp | 376 ++---------------- src/tool/try.cpp | 2 +- .../{typelist => meta}/configflagstest.cpp | 10 +- .../generatortest.cpp} | 12 +- .../{typelist => meta}/typelistmaniptest.cpp | 4 +- .../{typelist => meta}/typelisttest.cpp | 2 +- 16 files changed, 535 insertions(+), 409 deletions(-) create mode 100644 src/common/meta/configflags.hpp rename src/common/{typelistutil.hpp => meta/generator.hpp} (86%) rename src/common/{ => meta}/typelist.hpp (98%) create mode 100644 src/common/meta/typelistutil.hpp create mode 100644 src/common/meta/util.hpp rename tests/common/{typelist => meta}/configflagstest.cpp (97%) rename tests/common/{typelist/typelistutiltest.cpp => meta/generatortest.cpp} (94%) rename tests/common/{typelist => meta}/typelistmaniptest.cpp (98%) rename tests/common/{typelist => meta}/typelisttest.cpp (98%) diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp index 0968c88d4..e80ccfa8a 100644 --- a/src/common/configrules.hpp +++ b/src/common/configrules.hpp @@ -48,7 +48,7 @@ #include "common/p.hpp" #include "common/query.hpp" -#include "common/typelistutil.hpp" +#include "common/meta/generator.hpp" #include "common/singletonsubclass.hpp" //TODO: is it sensible to bring in the types explicitly here? (it's not necessary, but may be convenient...) diff --git a/src/common/meta/configflags.hpp b/src/common/meta/configflags.hpp new file mode 100644 index 000000000..daf8917de --- /dev/null +++ b/src/common/meta/configflags.hpp @@ -0,0 +1,180 @@ +/* + CONFIGFLAGS.hpp - Building classes based on configuration cases + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file configflags.hpp + ** Under some circumstances it is necessary to assemble functionality + ** out of elementary building blocks. Especially, this assembly can be + ** expressed as template specialisations directed by a configuration type. + ** Thus, the purpose of this header is to encode a flag-like configuraiton + ** as distinct types, which can be used to select such specialisations. + ** Each possible configuration can be encoded as a list of flags, which allows + ** to generate, filter and process those configurations. The final goal is to + ** automatically generate a factory which will deliver objects configured + ** according to the configuration in question. + ** + ** @see proc::engine::config::Strategy usage example + ** @see proc::engine::config::ConfigSelector + ** @see typelist.hpp + ** + */ + + +#ifndef LUMIERA_META_CONFIGFLAGS_H +#define LUMIERA_META_CONFIGFLAGS_H + + +#include "common/meta/typelist.hpp" + + +namespace lumiera { + namespace typelist { + + + template struct Flag { typedef Flag ID; }; + template<> struct Flag<0> { typedef NullType ID; }; + + + template< char f1=0 + , char f2=0 + , char f3=0 + , char f4=0 + , char f5=0 + > + struct Flags + { + typedef typename Types< typename Flag::ID + , typename Flag::ID + , typename Flag::ID + , typename Flag::ID + , typename Flag::ID + >::List + Tuple; + typedef Tuple List; + }; + + + template< char f1=0 + , char f2=0 + , char f3=0 + , char f4=0 + , char f5=0 + > + struct Config ///< distinct type representing a configuration + { + typedef typename Flags::Tuple Flags; + typedef Flags List; + }; + + + + template + struct ConfigSetFlag; ///< set (prepend) the Flag to the given config + + template< char Fl + , char f1 + , char f2 + , char f3 + , char f4 + , char IGN + > + struct ConfigSetFlag > + { + typedef Config Config; + }; + + + + /** build a configuration type for the given list-of-flags */ + template > + struct DefineConfigByFlags + { + typedef CONF Config; + typedef Config Type; + }; + template + struct DefineConfigByFlags< Node,FLAGS>, CONF> + { + typedef typename ConfigSetFlag< Fl + , typename DefineConfigByFlags::Config + >::Config Config; + typedef Config Type; + }; + + + /** + * Helper for calculating values and for + * invoking runtime code based on a given FlagTuple. + * Can also be used on a Typelist of several Configs. + * The latter case is typically used to invoke an operation + * while ennumerating all Flag-Configurations defined in Code. + * An example would be to build (at runtime) an dispatcher table. + * Explanation: For the Case covering a List of Configs, we provide + * a templated visitaion function, which can accept a functor object + * to be invoked on each Configuration. + */ + template + struct FlagInfo; + + template + struct FlagInfo, FLAGS> > + { + enum{ BITS = MAX(ff, FlagInfo::BITS) + , CODE = (1<::CODE + }; + }; + template<> + struct FlagInfo + { + enum{ BITS = 0 + , CODE = 0 + }; + + template + static typename FUNC::Ret + accept (FUNC& functor) + { + return functor.done(); + } + }; + template + struct FlagInfo > + { + typedef typename CONF::Flags ThisFlags; + enum{ BITS = MAX (char(FlagInfo::BITS), char(FlagInfo::BITS)) + }; + + template + static typename FUNC::Ret + accept (FUNC& functor) + { + functor.template visit(FlagInfo::CODE); + return FlagInfo::accept (functor); + } + }; + + + + } // namespace typelist + +} // namespace lumiera +#endif diff --git a/src/common/typelistutil.hpp b/src/common/meta/generator.hpp similarity index 86% rename from src/common/typelistutil.hpp rename to src/common/meta/generator.hpp index 3fb6a531e..59f340b5d 100644 --- a/src/common/typelistutil.hpp +++ b/src/common/meta/generator.hpp @@ -1,5 +1,5 @@ /* - TYPELISTUTIL.hpp - metaprogramming utilities for lists-of-types + GENERATOR.hpp - metaprogramming utilities for generating classes and interfaces Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -38,7 +38,7 @@ This code is heavily inspired by */ -/** @file typelistutil.hpp +/** @file generator.hpp ** Helpers for working with lumiera::typelist::Types (i.e. lists-of-types). ** The main purpose is to build interfaces and polymorphic implementations ** (using virtual functions) based on templated Types or Collections of types, @@ -50,10 +50,10 @@ This code is heavily inspired by */ -#ifndef LUMIERA_TYPELISTUTIL_H -#define LUMIERA_TYPELISTUTIL_H +#ifndef LUMIERA_META_GENERATOR_H +#define LUMIERA_META_GENERATOR_H -#include "common/typelist.hpp" +#include "common/meta/typelist.hpp" @@ -196,42 +196,6 @@ namespace lumiera }; - /** - * Metafunction counting the number of Types in the collection - */ - template - struct count; - template<> - struct count - { - enum{ value = 0 }; - }; - template - struct count > - { - enum{ value = 1 + count::value }; - }; - - /** - * Metafunction " max( sizeof(T) ) for T in TYPES " - */ - template - struct maxSize; - template<> - struct maxSize - { - enum{ value = 0 }; - }; - template - struct maxSize > - { - enum{ thisval = sizeof(TY) - , nextval = maxSize::value - , value = nextval > thisval? nextval:thisval - }; - }; - - } // namespace typelist } // namespace lumiera diff --git a/src/common/typelist.hpp b/src/common/meta/typelist.hpp similarity index 98% rename from src/common/typelist.hpp rename to src/common/meta/typelist.hpp index 2f45ae85b..c2d4568d0 100644 --- a/src/common/typelist.hpp +++ b/src/common/meta/typelist.hpp @@ -53,8 +53,8 @@ This code is heavily inspired by */ -#ifndef LUMIERA_TYPELIST_H -#define LUMIERA_TYPELIST_H +#ifndef LUMIERA_META_TYPELIST_H +#define LUMIERA_META_TYPELIST_H diff --git a/src/common/meta/typelistutil.hpp b/src/common/meta/typelistutil.hpp new file mode 100644 index 000000000..3a0d56284 --- /dev/null +++ b/src/common/meta/typelistutil.hpp @@ -0,0 +1,213 @@ +/* + TYPELISTUTIL.hpp - Utils for working with lists-of-types + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef LUMIERA_META_TYPELISTUTIL_H +#define LUMIERA_META_TYPELISTUTIL_H + + + +#include "common/meta/typelist.hpp" + +namespace lumiera { + namespace typelist { + + + /** + * Metafunction counting the number of Types in the collection + */ + template + struct count; + template<> + struct count + { + enum{ value = 0 }; + }; + template + struct count > + { + enum{ value = 1 + count::value }; + }; + + /** + * Metafunction " max( sizeof(T) ) for T in TYPES " + */ + template + struct maxSize; + template<> + struct maxSize + { + enum{ value = 0 }; + }; + template + struct maxSize > + { + enum{ thisval = sizeof(TY) + , nextval = maxSize::value + , value = nextval > thisval? nextval:thisval + }; + }; + + + /** apply a transformation (template) to each type in the list */ + template class _TRANS_> + struct Apply { typedef TY List; }; + + template< class TY, class TYPES + , template class _TRANS_ + > + struct Apply, _TRANS_ > { typedef Node< typename _TRANS_::Type + , typename Apply::List + > List; }; + + + template + struct CondNode { typedef TAIL Next; }; + + template + struct CondNode { typedef Node Next; }; + + /** filter away those types which don't fulfill a predicate metafunction */ + template< class TYPES + , template class _P_ ///< a template providing a boolean member \c ::value + > + struct Filter; + + template class _P_> + struct Filter { typedef NullType List; }; + + template< class TY, class TYPES + , template class _P_ + > + struct Filter,_P_> { typedef typename CondNode< _P_::value + , TY + , typename Filter::List + >::Next + List; }; + + + /** append lists-of-types */ + template + struct Append { typedef Node::List> List; }; + + template< class TY, class TYPES + , class TAIL + > + struct Append, TAIL> { typedef Node::List> List; }; + + template + struct Append > { typedef Node List; }; + + template + struct Append, NullType> { typedef Node List; }; + + template + struct Append { typedef Node List; }; + + template + struct Append { typedef Node List; }; + + template<> + struct Append { typedef NullType List; }; + + + + /** + * prefix each of the elements, + * yielding a list-of lists-of-types + */ + template + struct PrefixAll { typedef Node< typename Append::List, NullType> List; }; + + template + struct PrefixAll { typedef NullType List; }; + + template + struct PrefixAll { typedef Node< typename Append::List, NullType> List; }; + + template< class T + , class TY, class TYPES + > + struct PrefixAll > { typedef Node< typename Append::List + , typename PrefixAll::List + > List; }; + + + + + template + struct Distribute { typedef typename PrefixAll::List List; }; + + template + struct Distribute { typedef NullType List; }; + + template< class TY, class TYPES + , class TAIL + > + struct Distribute,TAIL> { typedef typename Append< typename PrefixAll::List + , typename Distribute::List + >::List + List; }; + + + + /** use a permutation generator + * for creating a list of all possible combinations + */ + template< class X + , template class _PERMU_> + struct Combine { typedef typename Distribute< typename _PERMU_::List + , Node + >::List List; }; + template< template class _PERMU_> + struct Combine { typedef Node List; }; + + template< class TY, class TYPES + , template class _PERMU_> + struct Combine,_PERMU_> { typedef typename Distribute< typename _PERMU_::List + , typename Combine::List + >::List List; }; + + /** permutation generator for the Combine metafunction, + * yielding an "on" and "off" case + */ + template + struct FlagOnOff + { + typedef Node > List; + }; + + + /** generate all possible on-off combinations of the given flags */ + template + struct CombineFlags + { + typedef typename Combine::List List; + }; + + + + + } // namespace typelist + +} // namespace lumiera +#endif diff --git a/src/common/meta/util.hpp b/src/common/meta/util.hpp new file mode 100644 index 000000000..3c2d985f2 --- /dev/null +++ b/src/common/meta/util.hpp @@ -0,0 +1,74 @@ +/* + UTIL.hpp - metaprogramming helpers and utilities + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef LUMIERA_META_UTIL_H +#define LUMIERA_META_UTIL_H + + + +namespace lumiera { + namespace typelist { + + + /* types for figuring out the overload resolution chosen by the compiler */ + + typedef char Yes_t; + struct No_t { char padding[8]; }; + + + /** semi-automatic detection if an instantiation is possible. + * Requires help by the template to be tested, which needs to define + * a typedef member \c is_defined. The embedded metafunction Test can be used + * as a predicate for filtering types which may yield a valid instantiation + * of the candidate template in question. + * \par + * A fully automated solution for this problem is impossible by theoretic reasons. + * Any non trivial use of such a \c is_defined trait would break the "One Definition Rule", + * as the state of any type can change from "partially defined" to "fully defined" over + * the course of any translation unit. Thus, even if there may be a \em solution out there, + * we can expect it to break at some point by improvements/fixes to the C++ Language. + */ + template class _CandidateTemplate_> + struct Instantiation + { + + template + class Test + { + typedef _CandidateTemplate_ Instance; + + template + static Yes_t check(typename U::is_defined *); + template + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(0))); + }; + }; + + + } // namespace typelist + +} // namespace lumiera +#endif diff --git a/src/common/variant.hpp b/src/common/variant.hpp index 561b7149c..48f813e19 100644 --- a/src/common/variant.hpp +++ b/src/common/variant.hpp @@ -41,7 +41,7 @@ #define LUMIERA_VARIANT_H -#include "common/typelistutil.hpp" +#include "common/meta/generator.hpp" #include diff --git a/src/common/visitor.hpp b/src/common/visitor.hpp index 3d22f11e6..49bbcedb5 100644 --- a/src/common/visitor.hpp +++ b/src/common/visitor.hpp @@ -79,7 +79,7 @@ Credits for many further implementation ideas go to #include "common/visitorpolicies.hpp" #include "common/visitordispatcher.hpp" -#include "common/typelist.hpp" +#include "common/meta/typelist.hpp" namespace lumiera diff --git a/src/common/wrapperptr.hpp b/src/common/wrapperptr.hpp index e17963eff..81afa1177 100644 --- a/src/common/wrapperptr.hpp +++ b/src/common/wrapperptr.hpp @@ -29,7 +29,7 @@ #include "common/variant.hpp" #include "common/accesscasted.hpp" -#include "common/typelist.hpp" +#include "common/meta/typelist.hpp" #include "proc/mobject/placement.hpp" #include "common/p.hpp" diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index 144050d39..40be62c35 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -62,7 +62,6 @@ #include "proc/engine/buffhandle.hpp" #include "proc/engine/bufftable.hpp" #include "proc/engine/nodeinvocation.hpp" -#include "proc/engine/nodewiringconfig.hpp" @@ -230,6 +229,18 @@ namespace engine { /* === declare the possible Assembly of these elementary steps === */ + enum Cases + { + CACHING = 1, + PROCESS, + INPLACE, + + NOT_SET = 0, + NUM_Cases = INPLACE + }; + + + template struct SelectBuffProvider; diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index 9223f5633..fe1a24856 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -19,369 +19,49 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - - +/** @file nodewiringconfig.hpp + ** Sometimes we need to coose a different implementation for dealing with + ** some special cases. While for simple cases, just testing a flag or using a + ** switch statement will do the job, matters get more difficult when we have to + ** employ a completely different execution path for each of the different cases, + ** while using a set of common building blocks. + ** \par + ** In the Lumiera render engine, right on the critical path, we need some glue code + ** for invoking the predecessor nodes when pulling a given processing node. The actual + ** sequence is quite dependant on the specific situation each node is wired up, regarding + ** buffer allocation, cache querying and the possible support for GPU processing and + ** render farms. The solution is to define specialisations of a Strategy template + ** using the specific configuration as template argument. Based on these, we can + ** create a collection of factories, which in turn will build the internal wiring + ** for the individual ProcNode instances in accordance to the situation determined + ** for this node, expressed as a set of flags. As a net result, each node has an + ** indivudual configuration (as opposed to creating a lot of hand-written individual + ** ProcNode subclasses), but parts of this configuration assembly is done already at + ** compile time, allowing for additional checks by typing and for possible optimisation. + ** + ** @see proc::engine::config::Strategy + ** @see nodewiring.hpp + ** @see configflags.hpp + ** + */ #ifndef ENGINE_NODEWIRINGCONFIG_H #define ENGINE_NODEWIRINGCONFIG_H #include "common/util.hpp" +#include "common/meta/configflags.hpp" -//#include -#include -#include -#include -#include -#include #include - - /////////////////TODO: library - -#include "common/typelistutil.hpp" -namespace lumiera { - namespace typelist { - - template class _TRANS_> - struct Apply { typedef TY List; }; - - template< class TY, class TYPES - , template class _TRANS_ - > - struct Apply, _TRANS_ > { typedef Node< typename _TRANS_::Type - , typename Apply::List - > List; }; - - - template - struct CondNode { typedef TAIL Next; }; - - template - struct CondNode { typedef Node Next; }; - - - template< class TYPES , template class _P_> - struct Filter; - - template class _P_> - struct Filter { typedef NullType List; }; - - template< class TY, class TYPES - , template class _P_ - > - struct Filter,_P_> { typedef typename CondNode< _P_::value - , TY - , typename Filter::List - >::Next - List; }; - - - - template - struct Append { typedef Node::List> List; }; - - template< class TY, class TYPES - , class TAIL - > - struct Append, TAIL> { typedef Node::List> List; }; - - template - struct Append > { typedef Node List; }; - - template - struct Append, NullType> { typedef Node List; }; - - template - struct Append { typedef Node List; }; - - template - struct Append { typedef Node List; }; - - template<> - struct Append { typedef NullType List; }; - - - - - template - struct PrefixAll { typedef Node< typename Append::List, NullType> List; }; - - template - struct PrefixAll { typedef NullType List; }; - - template - struct PrefixAll { typedef Node< typename Append::List, NullType> List; }; - - template< class T - , class TY, class TYPES - > - struct PrefixAll > { typedef Node< typename Append::List - , typename PrefixAll::List - > List; }; - - - - - template - struct Distribute { typedef typename PrefixAll::List List; }; - - template - struct Distribute { typedef NullType List; }; - - template< class TY, class TYPES - , class TAIL - > - struct Distribute,TAIL> { typedef typename Append< typename PrefixAll::List - , typename Distribute::List - >::List - List; }; - - - - - template< class X - , template class _PERMU_> - struct Combine { typedef typename Distribute< typename _PERMU_::List - , Node - >::List List; }; - template< template class _PERMU_> - struct Combine { typedef Node List; }; - - template< class TY, class TYPES - , template class _PERMU_> - struct Combine,_PERMU_> { typedef typename Distribute< typename _PERMU_::List - , typename Combine::List - >::List List; }; - - - template - struct FlagOnOff - { - typedef Node > List; - }; - - template - struct CombineFlags - { - typedef typename Combine::List List; - }; - - - - /////////// meta utils - - typedef char Yes_t; - struct No_t { char padding[8]; }; - - - /** semi-automatic detection if an instantiation is possible. - * Requires help by the template to be tested, which needs to define - * a typedef member \c is_defined. The embedded metafunction Test can be used - * as a predicate for filtering types which may yield a valid instantiation - * of the candidate template in question. - * \par - * A fully automated solution for this problem is impossible by theoretic reasons. - * Any non trivial use of such a \c is_defined trait would break the "One Definition Rule", - * as the state of any type can change from "partially defined" to "fully defined" over - * the course of any translation unit. Thus, even if there may be a \em solution out there, - * we can expect it to break at some point by improvements/fixes to the C++ Language. - */ - template class _CandidateTemplate_> - struct Instantiation - { - - template - class Test - { - typedef _CandidateTemplate_ Instance; - - template - static Yes_t check(typename U::is_defined *); - template - static No_t check(...); - - public: - static const bool value = (sizeof(Yes_t)==sizeof(check(0))); - }; - }; - - - - - - - template struct Flag { typedef Flag ID; }; - template<> struct Flag<0> { typedef NullType ID; }; - - - template< char f1=0 - , char f2=0 - , char f3=0 - , char f4=0 - , char f5=0 - > - struct Flags - { - typedef typename Types< typename Flag::ID - , typename Flag::ID - , typename Flag::ID - , typename Flag::ID - , typename Flag::ID - >::List - Tuple; - typedef Tuple List; - }; - - - template< char f1=0 - , char f2=0 - , char f3=0 - , char f4=0 - , char f5=0 - > - struct Config - { - typedef typename Flags::Tuple Flags; - typedef Flags List; - }; - - - template - struct ConfigSetFlag; - - template< char Fl - , char f1 - , char f2 - , char f3 - , char f4 - , char IGN - > - struct ConfigSetFlag > - { - typedef Config Config; - }; - - template > - struct DefineConfigByFlags - { - typedef CONF Config; - typedef Config Type; - }; - template - struct DefineConfigByFlags< Node,FLAGS>, CONF> - { - typedef typename ConfigSetFlag< Fl - , typename DefineConfigByFlags::Config - >::Config Config; - typedef Config Type; - }; - - - /** - * Helper for calculating values and for - * invoking runtime code based on a given FlagTuple. - * Can also be used on a Typelist of several Configs. - * The latter case is typically used to invoke an operation - * while ennumerating all Flag-Configurations defined in Code. - * An example would be to build (at runtime) an dispatcher table. - * Explanation: For the Case covering a List of Configs, we provide - * a templated visitaion function, which can accept a functor object - * to be invoked on each Configuration. - */ - template - struct FlagInfo; - - template - struct FlagInfo, FLAGS> > - { - enum{ BITS = MAX(ff, FlagInfo::BITS) - , CODE = (1<::CODE - }; - }; - template<> - struct FlagInfo - { - enum{ BITS = 0 - , CODE = 0 - }; - - template - static typename FUNC::Ret - accept (FUNC& functor) - { - return functor.done(); - } - }; - template - struct FlagInfo > - { - typedef typename CONF::Flags ThisFlags; - enum{ BITS = MAX (char(FlagInfo::BITS), char(FlagInfo::BITS)) - }; - - template - static typename FUNC::Ret - accept (FUNC& functor) - { - functor.template visit(FlagInfo::CODE); - return FlagInfo::accept (functor); - } - }; - - - - - - } // namespace typelist - -} // namespace lumiera - - /////////////////TODO: library(end) - +#include namespace engine { - - class WiringFactory; - - ///////TODO - using lumiera::typelist::Config; using lumiera::typelist::FlagInfo; - using lumiera::typelist::DefineConfigByFlags; - ///////TODO using boost::lexical_cast; using util::contains; - enum Cases - { - CACHING = 1, - PROCESS, - INPLACE, - - NOT_SET = 0, - NUM_Cases = INPLACE - }; - typedef std::bitset Bits; - - - - - /** - * Helper type used to describe the \em situation on which to base - * the selection of the wiring and configuration of a ProcNode. - * It is based on a tuple of elementary parameters (here of type bool); - * a combination of these describes the situation and is used to select - * a strategy, which in turn determines what concret operations to use - * while pulling the node, and how to combine these operations. - * @see engine::Strategy ///TODO namespace.... - */ -// template -// struct Config -// { -// static Bits getRepr () { return getCaseRepresentation(caching,process,inplace); } -// }; - /** * Helper for fabricating ProcNode Wiring configurations. @@ -439,7 +119,7 @@ namespace engine { }; - typedef boost::shared_ptr PFunc; + typedef std::tr1::shared_ptr PFunc; typedef std::map ConfigTable; ConfigTable possibleConfig_; ///< Table of factories diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 7d9d99000..3f51bcaf6 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -32,7 +32,7 @@ using boost::is_base_of; #include -#include "common/typelistutil.hpp" +#include "common/meta/generator.hpp" using lumiera::typelist::NullType; using lumiera::typelist::Node; using lumiera::typelist::Types; diff --git a/tests/common/typelist/configflagstest.cpp b/tests/common/meta/configflagstest.cpp similarity index 97% rename from tests/common/typelist/configflagstest.cpp rename to tests/common/meta/configflagstest.cpp index 1c9dbdf83..63ac834d9 100644 --- a/tests/common/typelist/configflagstest.cpp +++ b/tests/common/meta/configflagstest.cpp @@ -38,10 +38,10 @@ #include "common/test/run.hpp" -#include "common/typelistutil.hpp" +#include "common/meta/generator.hpp" +#include "common/meta/typelistutil.hpp" +#include "proc/engine/nodewiringconfig.hpp" #include "common/util.hpp" -//#include "common/typelist/typelistmanip.hpp" -#include "proc/engine/nodewiringconfig.hpp" ///////////TODO move to configflags.hpp #include #include @@ -343,6 +343,10 @@ namespace lumiera { }; + /** @test use the ConfigSelector template to build a set of factories, + * based on a set of configurations. Then invoke the apropriate + * factory by specifying the configuration bit code + */ void check_ConfigSelector() { cout << "\n==== check_ConfigSelector()\n"; diff --git a/tests/common/typelist/typelistutiltest.cpp b/tests/common/meta/generatortest.cpp similarity index 94% rename from tests/common/typelist/typelistutiltest.cpp rename to tests/common/meta/generatortest.cpp index 73cab939e..2909b076d 100644 --- a/tests/common/typelist/typelistutiltest.cpp +++ b/tests/common/meta/generatortest.cpp @@ -1,5 +1,5 @@ /* - TypeListUtil(Test) - check the typelist helpers + Generator(Test) - build an interface + implementation directed by a typelis Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -21,7 +21,7 @@ * *****************************************************/ -/** @file typelistutiltest.cpp +/** @file generatortest.cpp ** \par what are we doing here?? ** ** the following test composes both an interface and the corresponding implementation @@ -31,14 +31,14 @@ ** need to be absolutely fixed in the class definition) ** ** @see typelisttest.cpp - ** @see typelistutil.hpp + ** @see generator.hpp ** @see lumiera::query::ConfigRules a real world usage example ** */ #include "common/test/run.hpp" -#include "common/typelistutil.hpp" +#include "common/meta/generator.hpp" #include #include @@ -118,7 +118,7 @@ namespace lumiera * for a collection of classes * */ - class TypeListUtil_test : public Test + class TypeListGenerator_test : public Test { virtual void run(Arg arg) { @@ -144,7 +144,7 @@ namespace lumiera /** Register this test class... */ - LAUNCHER (TypeListUtil_test, "unit common"); + LAUNCHER (TypeListGenerator_test, "unit common"); diff --git a/tests/common/typelist/typelistmaniptest.cpp b/tests/common/meta/typelistmaniptest.cpp similarity index 98% rename from tests/common/typelist/typelistmaniptest.cpp rename to tests/common/meta/typelistmaniptest.cpp index 7b285d205..9d2321299 100644 --- a/tests/common/typelist/typelistmaniptest.cpp +++ b/tests/common/meta/typelistmaniptest.cpp @@ -39,9 +39,9 @@ #include "common/test/run.hpp" -#include "common/typelistutil.hpp" +#include "common/meta/generator.hpp" +#include "common/meta/typelistutil.hpp" #include "common/util.hpp" -#include "proc/engine/nodewiringconfig.hpp" ///////////TODO move to typelistutil.hpp #include #include diff --git a/tests/common/typelist/typelisttest.cpp b/tests/common/meta/typelisttest.cpp similarity index 98% rename from tests/common/typelist/typelisttest.cpp rename to tests/common/meta/typelisttest.cpp index 42159d96c..5c942b1e0 100644 --- a/tests/common/typelist/typelisttest.cpp +++ b/tests/common/meta/typelisttest.cpp @@ -22,7 +22,7 @@ #include "common/test/run.hpp" -#include "common/typelist.hpp" +#include "common/meta/typelist.hpp" #include From cf7bd178ded663d660cba6115d991f50680627ef Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 3 Aug 2008 16:47:38 +0200 Subject: [PATCH 18/40] WIP further refactorings. Put nodeoperation.hpp and ConfigSelector in a sub namespace engine::config --- src/common/meta/configflags.hpp | 12 +- src/common/meta/util.hpp | 16 +- src/common/variant.hpp | 1 + src/proc/engine/nodeoperation.hpp | 460 +++++++++++----------- src/proc/engine/nodewiring.cpp | 71 +++- src/proc/engine/nodewiring.hpp | 5 + src/proc/engine/nodewiringconfig.hpp | 233 +++++------ tests/common/meta/configflagstest.cpp | 76 +--- tests/common/meta/typelistdiagnostics.hpp | 140 +++++++ 9 files changed, 589 insertions(+), 425 deletions(-) create mode 100644 tests/common/meta/typelistdiagnostics.hpp diff --git a/src/common/meta/configflags.hpp b/src/common/meta/configflags.hpp index daf8917de..7ffb79a70 100644 --- a/src/common/meta/configflags.hpp +++ b/src/common/meta/configflags.hpp @@ -25,12 +25,18 @@ ** Under some circumstances it is necessary to assemble functionality ** out of elementary building blocks. Especially, this assembly can be ** expressed as template specialisations directed by a configuration type. - ** Thus, the purpose of this header is to encode a flag-like configuraiton + ** Thus, the purpose of this header is to encode flag-like configuraitons ** as distinct types, which can be used to select such specialisations. ** Each possible configuration can be encoded as a list of flags, which allows ** to generate, filter and process those configurations. The final goal is to - ** automatically generate a factory which will deliver objects configured - ** according to the configuration in question. + ** automatically generate a factory which is able to deliver objects + ** configured according to the situation encoded in the flags. + ** + ** @note currently there is an inherent limitation to configurations defined by + ** a maximum of 5 independant flags. While it is easy to increase this limit, + ** you should consider that the final goal is to genarate template instantiations, + ** which would lead to more and more code bloat with growing number of possible + ** combination. ** ** @see proc::engine::config::Strategy usage example ** @see proc::engine::config::ConfigSelector diff --git a/src/common/meta/util.hpp b/src/common/meta/util.hpp index 3c2d985f2..b01a412fa 100644 --- a/src/common/meta/util.hpp +++ b/src/common/meta/util.hpp @@ -27,15 +27,17 @@ namespace lumiera { + + + /* types for figuring out the overload resolution chosen by the compiler */ + + typedef char Yes_t; + struct No_t { char padding[8]; }; + + + namespace typelist { - - /* types for figuring out the overload resolution chosen by the compiler */ - - typedef char Yes_t; - struct No_t { char padding[8]; }; - - /** semi-automatic detection if an instantiation is possible. * Requires help by the template to be tested, which needs to define * a typedef member \c is_defined. The embedded metafunction Test can be used diff --git a/src/common/variant.hpp b/src/common/variant.hpp index 48f813e19..751c2d9fc 100644 --- a/src/common/variant.hpp +++ b/src/common/variant.hpp @@ -41,6 +41,7 @@ #define LUMIERA_VARIANT_H +#include "common/meta/typelistutil.hpp" #include "common/meta/generator.hpp" #include diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index 40be62c35..b413c15bb 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -63,243 +63,249 @@ #include "proc/engine/bufftable.hpp" #include "proc/engine/nodeinvocation.hpp" +#include "common/meta/util.hpp" +#include "common/meta/configflags.hpp" + namespace engine { - - - /** - * Collection of functions used to build up the invocation sequence. - */ - class OperationBase - { - - }; + namespace config { - template - struct QueryCache : NEXT - { - BuffHandle - step (Invocation& ivo) - { - BuffHandle fetched = ivo.fetch ( - this->genFrameID (ivo)); - if (fetched) - return fetched; - else - return NEXT::step (ivo); - } - }; - - - template - struct AllocBufferTable : NEXT - { - BuffHandle - step (Invocation& ivo) - { - BuffTableChunk buffTab (ivo.wiring, ivo.getBuffTableStorage()); - ivo.setBuffTab(&buffTab); - ASSERT (ivo.buffTab); - ASSERT (ivo.buffTab_isConsistent()); - - return NEXT::step (ivo); - } - }; - - - template - struct PullInput : NEXT - { - BuffHandle - step (Invocation& ivo) - { - BuffHandle * inH = ivo.buffTab->inHandle; - BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff; - - for (uint i = 0; i < ivo.nrI(); ++i ) - { - inBuff[i] = - *(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor - // now Input #i is ready... - } - return NEXT::step (ivo); // note: passing down a ref to the ProcessInvocation - } - }; - - - template - struct ReadSource : NEXT - { - BuffHandle - step (Invocation& ivo) - { - BuffHandle *inH = ivo.buffTab->inHandle; - BuffHandle *outH = ivo.buffTab->outHandle; - BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff; - BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff; - - ASSERT (ivo.nrO() == ivo.nrI() ); - - for (uint i = 0; i < ivo.nrI(); ++i ) - { - inBuff[i] = outBuff[i] = - *(inH[i] = outH[i] = this->getSource(ivo,i)); - // now Input #i is ready... - } - return NEXT::step (ivo); - } - }; - - - template - struct AllocOutput : NEXT - { - BuffHandle - step (Invocation& ivo) - { - ASSERT (ivo.buffTab); - ASSERT (ivo.nrO() < ivo.buffTabSize()); - BuffHandle *outH = ivo.buffTab->outHandle; - BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff; - - for (uint i = 0; i < ivo.nrO(); ++i ) - { - outBuff[i] = - *(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType)); - // now Output buffer for channel #i is available... - } - return NEXT::step (ivo); - } - }; - - - template - struct ProcessData : NEXT - { - BuffHandle - step (Invocation& ivo) - { - ASSERT (ivo.buffTab); - ASSERT (ivo.buffTab_isConsistent()); - ASSERT (this->validateBuffers(ivo)); - - // Invoke our own process() function, - // providing the array of outBuffer+inBuffer ptrs - ivo.wiring.processFunction (ivo.buffTab->outBuff); - - return NEXT::step (ivo); - } - }; - - template - struct FeedCache : NEXT - { - BuffHandle - step (Invocation& ivo) - { - for (uint i = 0; i < ivo.nrO(); ++i ) - { - // declare all Outputs as finished - ivo.is_calculated(ivo.buffTab->outHandle[i]); - } - - return NEXT::step (ivo); - } - }; - - template - struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor?? - { ///////////////// this would require BuffHandle to be a smart ref.... - BuffHandle - step (Invocation& ivo) - { - // all buffers besides the required Output no longer needed - this->releaseBuffers(ivo.buffTab->outHandle, - ivo.buffTabSize(), - ivo.outNr); - - return ivo.buffTab->outHandle[ivo.outNr]; - } - }; - - - - - /* === declare the possible Assembly of these elementary steps === */ - - enum Cases - { - CACHING = 1, - PROCESS, - INPLACE, + + /** + * Collection of functions used to build up the invocation sequence. + */ + class OperationBase + { + typedef lumiera::Yes_t is_defined; + }; - NOT_SET = 0, - NUM_Cases = INPLACE - }; + template + struct QueryCache : NEXT + { + BuffHandle + step (Invocation& ivo) + { + BuffHandle fetched = ivo.fetch ( + this->genFrameID (ivo)); + if (fetched) + return fetched; + else + return NEXT::step (ivo); + } + }; + + + template + struct AllocBufferTable : NEXT + { + BuffHandle + step (Invocation& ivo) + { + BuffTableChunk buffTab (ivo.wiring, ivo.getBuffTableStorage()); + ivo.setBuffTab(&buffTab); + ASSERT (ivo.buffTab); + ASSERT (ivo.buffTab_isConsistent()); + + return NEXT::step (ivo); + } + }; + + + template + struct PullInput : NEXT + { + BuffHandle + step (Invocation& ivo) + { + BuffHandle * inH = ivo.buffTab->inHandle; + BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff; + + for (uint i = 0; i < ivo.nrI(); ++i ) + { + inBuff[i] = + *(inH[i] = this->pullPredecessor(ivo,i)); // invoke predecessor + // now Input #i is ready... + } + return NEXT::step (ivo); // note: passing down a ref to the ProcessInvocation + } + }; + + + template + struct ReadSource : NEXT + { + BuffHandle + step (Invocation& ivo) + { + BuffHandle *inH = ivo.buffTab->inHandle; + BuffHandle *outH = ivo.buffTab->outHandle; + BuffHandle::PBuff *inBuff = ivo.buffTab->inBuff; + BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff; + + ASSERT (ivo.nrO() == ivo.nrI() ); + + for (uint i = 0; i < ivo.nrI(); ++i ) + { + inBuff[i] = outBuff[i] = + *(inH[i] = outH[i] = this->getSource(ivo,i)); + // now Input #i is ready... + } + return NEXT::step (ivo); + } + }; + + + template + struct AllocOutput : NEXT + { + BuffHandle + step (Invocation& ivo) + { + ASSERT (ivo.buffTab); + ASSERT (ivo.nrO() < ivo.buffTabSize()); + BuffHandle *outH = ivo.buffTab->outHandle; + BuffHandle::PBuff *outBuff = ivo.buffTab->outBuff; + + for (uint i = 0; i < ivo.nrO(); ++i ) + { + outBuff[i] = + *(outH[i] = ivo.allocateBuffer (ivo.wiring.out[i].bufferType)); + // now Output buffer for channel #i is available... + } + return NEXT::step (ivo); + } + }; + + + template + struct ProcessData : NEXT + { + BuffHandle + step (Invocation& ivo) + { + ASSERT (ivo.buffTab); + ASSERT (ivo.buffTab_isConsistent()); + ASSERT (this->validateBuffers(ivo)); + + // Invoke our own process() function, + // providing the array of outBuffer+inBuffer ptrs + ivo.wiring.processFunction (ivo.buffTab->outBuff); + + return NEXT::step (ivo); + } + }; + + template + struct FeedCache : NEXT + { + BuffHandle + step (Invocation& ivo) + { + for (uint i = 0; i < ivo.nrO(); ++i ) + { + // declare all Outputs as finished + ivo.is_calculated(ivo.buffTab->outHandle[i]); + } + + return NEXT::step (ivo); + } + }; + + template + struct ReleaseBuffers : NEXT /////////////////TODO: couldn't this be done automatically by BuffTab's dtor?? + { ///////////////// this would require BuffHandle to be a smart ref.... + BuffHandle + step (Invocation& ivo) + { + // all buffers besides the required Output no longer needed + this->releaseBuffers(ivo.buffTab->outHandle, + ivo.buffTabSize(), + ivo.outNr); + + return ivo.buffTab->outHandle[ivo.outNr]; + } + }; + + + + + /* === declare the possible Assembly of these elementary steps === */ + + enum Cases + { + CACHING = 1, + PROCESS, + INPLACE, + + NOT_SET = 0, + NUM_Cases = INPLACE + }; + + + + template + struct SelectBuffProvider; + + template<> struct SelectBuffProvider : AllocBufferFromCache { }; + template<> struct SelectBuffProvider : AllocBufferFromParent{ }; + template<> struct SelectBuffProvider : AllocBufferFromCache { }; + template<> struct SelectBuffProvider<> : AllocBufferFromParent{ }; + + template + struct Strategy ; - - template - struct SelectBuffProvider; - - template<> struct SelectBuffProvider : AllocBufferFromCache { }; - template<> struct SelectBuffProvider : AllocBufferFromParent{ }; - template<> struct SelectBuffProvider : AllocBufferFromCache { }; - template<> struct SelectBuffProvider<> : AllocBufferFromParent{ }; - - - template - struct Strategy ; - - using lumiera::typelist::Config; - - template - struct Strategy< Config > - : QueryCache< - AllocBufferTable< - PullInput< - AllocOutput< - ProcessData< - FeedCache< + using lumiera::typelist::Config; + + template + struct Strategy< Config > + : QueryCache< + AllocBufferTable< + PullInput< + AllocOutput< + ProcessData< + FeedCache< + ReleaseBuffers< + OperationBase > > > > > > > + { }; + + template + struct Strategy< Config > + : AllocBufferTable< + PullInput< + AllocOutput< + ProcessData< ReleaseBuffers< - OperationBase > > > > > > > - { }; - - template - struct Strategy< Config > - : AllocBufferTable< - PullInput< - AllocOutput< - ProcessData< + OperationBase > > > > > + { }; + + template<> + struct Strategy< Config<> > + : AllocBufferTable< + ReadSource< ReleaseBuffers< - OperationBase > > > > > - { }; - - template<> - struct Strategy< Config<> > - : AllocBufferTable< - ReadSource< - ReleaseBuffers< - OperationBase > > > - { }; - - template<> - struct Strategy< Config > : Strategy< Config<> > { }; - - template<> - struct Strategy< Config > - : AllocBufferTable< - ReadSource< - AllocOutput< - ProcessData< // wiring_.processFunction is supposed to do just buffer copying here - ReleaseBuffers< - OperationBase > > > > > - { }; - - - + OperationBase > > > + { }; + + template<> + struct Strategy< Config > : Strategy< Config<> > { }; + + template<> + struct Strategy< Config > + : AllocBufferTable< + ReadSource< + AllocOutput< + ProcessData< // wiring_.processFunction is supposed to do just buffer copying here + ReleaseBuffers< + OperationBase > > > > > + { }; + + + + + } // namespace config } // namespace engine #endif diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp index a9cafa1a8..dfbe534f9 100644 --- a/src/proc/engine/nodewiring.cpp +++ b/src/proc/engine/nodewiring.cpp @@ -26,9 +26,77 @@ #include "proc/engine/nodeoperation.hpp" #include "proc/engine/nodewiringconfig.hpp" +#include "common/meta/typelistutil.hpp" + namespace engine { + namespace { // internal: setting up a factory for each required configuration + + using config::CACHING; + using config::PROCESS; + using config::INPLACE; + + using config::ConfigSelector; + using config::Strategy; + + using lumiera::typelist::Flags; + using lumiera::typelist::CombineFlags; + using lumiera::typelist::DefineConfigByFlags; + using lumiera::typelist::Instantiation; + using lumiera::typelist::Apply; + using lumiera::typelist::Filter; + + + typedef Flags::Tuple AllFlags; + + /** build the list of all possible flag combinations */ + typedef CombineFlags AllFlagCombinations; + + /** build a configuration type for each of those flag combinations */ + typedef Apply AllConfigs; + + /** filter those configurations which actually define a wiring strategy */ + typedef Filter::Test> PossibleConfigs; + + + class Alloc {}; ///////////////TODO + + + template + class WiringDescriptorFactory + { + Alloc& alloc_; + + public: + WiringDescriptorFactory(Alloc& a) + : alloc_(a) {} + + WiringDescriptor& + operator() () + { + /////////////////////////////////////////////TODO + //return offset_ + Maybe::CODE; + } + }; + + + typedef ConfigSelector WiringSelector; + + struct WiringFactoryImpl + { +// WiringSelector selector; + + WiringFactoryImpl (Alloc& alloc) +// : selector(PossibleConfigs::List(), alloc) + { } + }; + + } // (END) internals + + + /////////////////////////////TODO: define the ctor + /** create and configure a concrete wiring descriptor to tie * a ProcNode to its predecessor nodes. This includes selecting @@ -47,7 +115,8 @@ namespace engine { UNIMPLEMENTED ("build the actual wiring descriptor based on given operation options"); // Bits config (FlagInfo::CODE); -// return selector(config); + size_t config = 13; /////////////////////////////////////////TODO +// return pImpl_->selector(config); } // BlockAlloc > > >::fabricate(); diff --git a/src/proc/engine/nodewiring.hpp b/src/proc/engine/nodewiring.hpp index d17b95396..2d1e587a4 100644 --- a/src/proc/engine/nodewiring.hpp +++ b/src/proc/engine/nodewiring.hpp @@ -27,6 +27,7 @@ #include "proc/engine/procnode.hpp" +#include #include @@ -36,6 +37,8 @@ namespace engine { class WiringFactory; + namespace { class WiringFactoryImpl; } + /** * Actual implementation of the link between nodes, @@ -73,6 +76,8 @@ namespace engine { class WiringFactory { + boost::scoped_ptr pImpl_; + public: WiringDescriptor& operator() (uint nrOut, uint nrIn, bool cache); diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index fe1a24856..c6df0c0db 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -56,122 +56,125 @@ namespace engine { - - using lumiera::typelist::FlagInfo; + namespace config { - using boost::lexical_cast; - using util::contains; - - - /** - * Helper for fabricating ProcNode Wiring configurations. - * This object builds a table of factories, holding one factory - * for each possible node configuration. Provided with the desired - * configuration encoded as bits, the related factory can be invoked, - * thus producing a product object for the given configuration. - * - * \par implementation notes - * The actual factory class is templated, so it will be defined - * at the use site of ConfigSelector. Moreover, this factory - * usually expects an ctor argument, which will be fed through - * when creating the ConfigSelector instance. This is one of - * the reasons why we go through all these complicated factory - * building: this ctor argument usually brings in a reference - * to the actual memory allocator. Thus we have to rebuild the - * ConfigSelector each time we switch and rebuild the ProcNode - * factories, which in turn happens each time we use a new - * bulk allocation memory block -- typically for each separate - * segment of the Timeline and processing node graph. - * - * Now the selection of the possible flag configurations, for which - * Factory instances are created in the table, is governed by the - * type parameter of the ConfigSelector ctor. This type parameter - * needs to be a Typelist of Typelists, each representing a flag - * configuration. The intention is to to drive this selection by - * the use of template metaprogramming for extracting all - * currently defined StateProxy object configurations. - * @todo as the facories live only within the enclosed table (map) - * we could allocate them in-place. Unfortunately this is - * non-trivial, because the stl containers employ - * value semantics and thus do a copy even on insert. - * Thus, for now we use a shared_ptr to hold the factory - * heap allocated. - */ - template< template class Factory - , typename PAR ///< ctor parameter of the Factories - , typename RET ///< common base class of the Factory's products - > - class ConfigSelector - { - - struct FacFunctor - { - virtual ~FacFunctor() {} - virtual RET invoke() =0; - }; - template - struct FactoryHolder : FacFunctor - { - FAC factory_; - FactoryHolder(PAR p) : factory_(p) {} - - virtual RET invoke () { return factory_(); } - }; - - - typedef std::tr1::shared_ptr PFunc; - typedef std::map ConfigTable; - - ConfigTable possibleConfig_; ///< Table of factories - - /** Helper: a visitor usable with FlagInfo */ - struct FactoryTableBuilder - { - PAR ctor_param_; - ConfigTable& factories_; - - FactoryTableBuilder (ConfigTable& tab, PAR p) - : ctor_param_(p), - factories_(tab) { } - - - /* === visitation interface === */ - - typedef void Ret; - - template - void - visit (size_t code) - { - PFunc pFactory (new FactoryHolder > (ctor_param_)); - factories_[code] = pFactory; - } - - void done() {} - }; - - public: - template - ConfigSelector(CONFS const&, PAR factory_ctor_param) - { - FactoryTableBuilder buildTable(this->possibleConfig_, - factory_ctor_param ); - // store a new Factory instance - // for each possible Flag-Configuration - FlagInfo::accept (buildTable); - } - - RET - operator() (size_t configFlags) ///< invoke the factory corresponding to the given config - { - if (contains (possibleConfig_, configFlags)) - return possibleConfig_[configFlags]->invoke(); - else - throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="+lexical_cast(configFlags)); - } - }; - - + using lumiera::typelist::FlagInfo; + + using boost::lexical_cast; + using util::contains; + + + /** + * Helper for fabricating ProcNode Wiring configurations. + * This object builds a table of factories, holding one factory + * for each possible node configuration. Provided with the desired + * configuration encoded as bits, the related factory can be invoked, + * thus producing a product object for the given configuration. + * + * \par implementation notes + * The actual factory class is templated, so it will be defined + * at the use site of ConfigSelector. Moreover, this factory + * usually expects an ctor argument, which will be fed through + * when creating the ConfigSelector instance. This is one of + * the reasons why we go through all these complicated factory + * building: this ctor argument usually brings in a reference + * to the actual memory allocator. Thus we have to rebuild the + * ConfigSelector each time we switch and rebuild the ProcNode + * factories, which in turn happens each time we use a new + * bulk allocation memory block -- typically for each separate + * segment of the Timeline and processing node graph. + * + * Now the selection of the possible flag configurations, for which + * Factory instances are created in the table, is governed by the + * type parameter of the ConfigSelector ctor. This type parameter + * needs to be a Typelist of Typelists, each representing a flag + * configuration. The intention is to to drive this selection by + * the use of template metaprogramming for extracting all + * currently defined StateProxy object configurations. + * @todo as the facories live only within the enclosed table (map) + * we could allocate them in-place. Unfortunately this is + * non-trivial, because the stl containers employ + * value semantics and thus do a copy even on insert. + * Thus, for now we use a shared_ptr to hold the factory + * heap allocated. + */ + template< template class Factory + , typename PAR ///< ctor parameter of the Factories + , typename RET ///< common base class of the Factory's products + > + class ConfigSelector + { + + struct FacFunctor + { + virtual ~FacFunctor() {} + virtual RET invoke() =0; + }; + template + struct FactoryHolder : FacFunctor + { + FAC factory_; + FactoryHolder(PAR p) : factory_(p) {} + + virtual RET invoke () { return factory_(); } + }; + + + typedef std::tr1::shared_ptr PFunc; + typedef std::map ConfigTable; + + ConfigTable possibleConfig_; ///< Table of factories + + /** Helper: a visitor usable with FlagInfo */ + struct FactoryTableBuilder + { + PAR ctor_param_; + ConfigTable& factories_; + + FactoryTableBuilder (ConfigTable& tab, PAR p) + : ctor_param_(p), + factories_(tab) { } + + + /* === visitation interface === */ + + typedef void Ret; + + template + void + visit (size_t code) + { + PFunc pFactory (new FactoryHolder > (ctor_param_)); + factories_[code] = pFactory; + } + + void done() {} + }; + + public: + template + ConfigSelector(CONFS const&, PAR factory_ctor_param) + { + FactoryTableBuilder buildTable(this->possibleConfig_, + factory_ctor_param ); + // store a new Factory instance + // for each possible Flag-Configuration + FlagInfo::accept (buildTable); + } + + RET + operator() (size_t configFlags) ///< invoke the factory corresponding to the given config + { + if (contains (possibleConfig_, configFlags)) + return possibleConfig_[configFlags]->invoke(); + else + throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="+lexical_cast(configFlags)); + } + }; + + + + } // namespace config } // namespace engine #endif diff --git a/tests/common/meta/configflagstest.cpp b/tests/common/meta/configflagstest.cpp index 63ac834d9..cf5660361 100644 --- a/tests/common/meta/configflagstest.cpp +++ b/tests/common/meta/configflagstest.cpp @@ -38,8 +38,11 @@ #include "common/test/run.hpp" +#include "common/meta/util.hpp" #include "common/meta/generator.hpp" #include "common/meta/typelistutil.hpp" +#include "common/meta/configflags.hpp" +#include "meta/typelistdiagnostics.hpp" #include "proc/engine/nodewiringconfig.hpp" #include "common/util.hpp" @@ -69,77 +72,6 @@ namespace lumiera { , NOT_SET = 0 }; - format fmt ("-<%u>%s"); - - struct NullP - { - static string print () { return "-"; } - }; - - /** debugging template, - * printing the "number" used for intantiation on ctor call - */ - template - struct Printer; - - template - struct Printer - : BASE - { - static string print () { return str( fmt % "·" % BASE::print()); } - }; - - template - struct Printer, BASE> - : BASE - { - static string print () { return str( fmt % uint(Fl) % BASE::print()); } - }; - - - - /** call the debug-print for a typelist - * utilizing the Printer template */ - template - string - printSublist () - { - typedef InstantiateChained SubList; - return SubList::print(); - } - - /** Spezialisation for debug-printing of a nested sublist */ - template - struct Printer, BASE> - : BASE - { - static string print () - { - typedef Node List; - return string("\n\t+--") + printSublist()+"+" - + BASE::print(); - } - }; - - template - struct Printer, BASE> - : BASE - { - static string print () - { - typedef typename Config::Flags FlagList; - return string("\n\t+-Conf-[") + printSublist()+"]" - + BASE::print(); - } - }; - - -#define DIAGNOSE(LIST) \ - typedef InstantiateChained Contents_##LIST; - -#define DISPLAY(NAME) \ - DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n"; - /* === Test data === */ @@ -355,7 +287,7 @@ namespace lumiera { typedef Filter::Test> Possible_Configs; DISPLAY (Possible_Configs); - typedef engine::ConfigSelector TestFactorySelector; + typedef engine::config::ConfigSelector TestFactorySelector; const long offset = 1000; // parameter fed to all TestFactory ctors TestFactorySelector testConfigSelector (Possible_Configs::List(), offset); diff --git a/tests/common/meta/typelistdiagnostics.hpp b/tests/common/meta/typelistdiagnostics.hpp new file mode 100644 index 000000000..bccc05ab2 --- /dev/null +++ b/tests/common/meta/typelistdiagnostics.hpp @@ -0,0 +1,140 @@ +/* + TYPELISTDIAGNOSTICS - helper for testing the typelist based utilities + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +/** @file typelistdiagnostics.hpp + ** a Printer template usable for debugging the structure of a typelist built + ** upon some simple debugging-style types. Examples being a Num template, + ** or the Flag type. A Printer type generated from this template provides + ** a static \c print() function returing a string visualizing the structure + ** of the typelist provided as parameter to the Printer template. + ** + ** @see typelistmaniptest.cpp + ** @see configflagstest.cpp + ** + */ +#ifndef META_TYPELISTDIAGNOSTICS_H +#define META_TYPELISTDIAGNOSTICS_H + + +#include "common/meta/generator.hpp" +//#include "common/meta/typelistutil.hpp" +//#include "common/meta/util.hpp" +//#include "common/util.hpp" + +#include + +using std::string; +using boost::format; + + +namespace lumiera { + namespace typelist { + namespace test { + + + namespace { // internal definitions + + + format fmt ("-<%u>%s"); + + struct NullP + { + static string print () { return "-"; } + }; + + /** debugging template, + * printing the "number" used for intantiation on ctor call + */ + template + struct Printer; + + template + struct Printer + : BASE + { + static string print () { return str( fmt % "·" % BASE::print()); } + }; + + template + struct Printer, BASE> + : BASE + { + static string print () { return str( fmt % uint(Fl) % BASE::print()); } + }; + + + + /** call the debug-print for a typelist + * utilizing the Printer template */ + template + string + printSublist () + { + typedef InstantiateChained SubList; + return SubList::print(); + } + + /** Spezialisation for debug-printing of a nested sublist */ + template + struct Printer, BASE> + : BASE + { + static string print () + { + typedef Node List; + return string("\n\t+--") + printSublist()+"+" + + BASE::print(); + } + }; + + template + struct Printer, BASE> + : BASE + { + static string print () + { + typedef typename Config::Flags FlagList; + return string("\n\t+-Conf-[") + printSublist()+"]" + + BASE::print(); + } + }; + + +#define DIAGNOSE(LIST) \ + typedef InstantiateChained Contents_##LIST; + +#define DISPLAY(NAME) \ + DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n"; + + + + } // (End) internal defs + + + + } // namespace test + + } // namespace typelist + +} // namespace lumiera +#endif From 29e33e4eb4afe8c3b8345e81aaa55df8985f6ba6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 4 Aug 2008 05:42:55 +0200 Subject: [PATCH 19/40] generating (wiring) configs now considered final --- src/proc/engine/nodefactory.hpp | 6 +- src/proc/engine/nodeinvocation.hpp | 8 +- src/proc/engine/nodeoperation.hpp | 8 +- src/proc/engine/nodewiring.cpp | 51 +++-- src/proc/engine/nodewiring.hpp | 3 +- src/proc/engine/nodewiringconfig.hpp | 4 +- src/proc/engine/procnode.hpp | 9 +- .../builder/applicablebuildertargettypes.hpp | 2 +- src/proc/mobject/builder/operationpoint.hpp | 2 +- tests/50components.tests | 205 +++++++++++++++++- tests/common/meta/configflagstest.cpp | 20 +- tests/common/meta/typelistdiagnostics.hpp | 38 +++- tests/common/meta/typelistmaniptest.cpp | 73 +------ 13 files changed, 308 insertions(+), 121 deletions(-) diff --git a/src/proc/engine/nodefactory.hpp b/src/proc/engine/nodefactory.hpp index 8dbcc5f0c..7abae3340 100644 --- a/src/proc/engine/nodefactory.hpp +++ b/src/proc/engine/nodefactory.hpp @@ -47,7 +47,7 @@ namespace engine { using std::vector; class Trafo; - typedef Trafo* PTrafo; ///< @todo handle ProcNode by pointer or by shared-ptr?? + typedef Trafo* PTrafo; ///< @note ProcNode is handled by pointer and bulk allocated/dealocated /** @@ -55,10 +55,6 @@ namespace engine { */ class NodeFactory { - /** custom deleter func allowing a smart-ptr - * to take ownership of processing nodes - */ - static void deleterFunc (ProcNode* pno) { delete pno; } public: diff --git a/src/proc/engine/nodeinvocation.hpp b/src/proc/engine/nodeinvocation.hpp index 2b5c0afbb..8b350d382 100644 --- a/src/proc/engine/nodeinvocation.hpp +++ b/src/proc/engine/nodeinvocation.hpp @@ -23,8 +23,8 @@ /** @file nodeinvocation.hpp ** Organize the state related to the invocation of s single ProcNode::pull() call ** This header defines part of the "glue" which holds together the render node network - ** and enables to pull a result frames from the nodes. Doing so requires some invocation - ** local state tobe maintained, especially a table of buffers used to carry out the + ** and enables to pull result frames from the nodes. Doing so requires some invocation + ** local state to be maintained, especially a table of buffers used to carry out the ** calculations. Further, getting the input buffers filled requires to issue recursive ** \c pull() calls, which on the whole creates a stack-like assembly of local invocation ** state. @@ -37,7 +37,7 @@ ** ** \par composition of the Invocation State ** For each individual ProcNode#pull() call, the WiringAdapter#callDown() builds an StateAdapter - ** instance directly on the stack, holding the actual buffer pointers and state references. Using this + ** instance directly on the stack, managing the actual buffer pointers and state references. Using this ** StateAdapter, the predecessor nodes are pulled. The way these operations are carried out is encoded ** in the actual StateAdapter type known to the NodeWiring (WiringAdapter) instance. All of these actual ** StateAdapter types are built as implementing the engine::State interface. @@ -196,7 +196,7 @@ namespace engine { { public: ActualInvocationProcess (State& callingProcess, WiringDescriptor const& w, const uint outCh) - : Invocation(callingProcess, w, outCh) + : BufferProvider(callingProcess, w, outCh) { } /** contains the details of Cache query and recursive calls diff --git a/src/proc/engine/nodeoperation.hpp b/src/proc/engine/nodeoperation.hpp index b413c15bb..b30c6cbeb 100644 --- a/src/proc/engine/nodeoperation.hpp +++ b/src/proc/engine/nodeoperation.hpp @@ -248,10 +248,10 @@ namespace engine { template struct SelectBuffProvider; - template<> struct SelectBuffProvider : AllocBufferFromCache { }; - template<> struct SelectBuffProvider : AllocBufferFromParent{ }; - template<> struct SelectBuffProvider : AllocBufferFromCache { }; - template<> struct SelectBuffProvider<> : AllocBufferFromParent{ }; + template<> struct SelectBuffProvider { typedef AllocBufferFromCache Type; }; + template<> struct SelectBuffProvider { typedef AllocBufferFromParent Type; }; + template<> struct SelectBuffProvider { typedef AllocBufferFromCache Type; }; + template<> struct SelectBuffProvider<> { typedef AllocBufferFromParent Type; }; template diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp index dfbe534f9..6a20f86e4 100644 --- a/src/proc/engine/nodewiring.cpp +++ b/src/proc/engine/nodewiring.cpp @@ -30,15 +30,8 @@ namespace engine { - - namespace { // internal: setting up a factory for each required configuration - - using config::CACHING; - using config::PROCESS; - using config::INPLACE; - - using config::ConfigSelector; - using config::Strategy; + + namespace config { using lumiera::typelist::Flags; using lumiera::typelist::CombineFlags; @@ -49,16 +42,24 @@ namespace engine { typedef Flags::Tuple AllFlags; - + /** build the list of all possible flag combinations */ typedef CombineFlags AllFlagCombinations; - + /** build a configuration type for each of those flag combinations */ typedef Apply AllConfigs; /** filter those configurations which actually define a wiring strategy */ typedef Filter::Test> PossibleConfigs; + } // namespace config + + + + + namespace { // internal: setting up a factory for each required configuration + + using config::ConfigSelector; class Alloc {}; ///////////////TODO @@ -67,7 +68,7 @@ namespace engine { class WiringDescriptorFactory { Alloc& alloc_; - + public: WiringDescriptorFactory(Alloc& a) : alloc_(a) {} @@ -75,20 +76,32 @@ namespace engine { WiringDescriptor& operator() () { - /////////////////////////////////////////////TODO - //return offset_ + Maybe::CODE; + /////////////////////////////////////////////TODO + + typedef config::Strategy Strategy; + typedef typename config::SelectBuffProvider<>::Type BuffProvider; ////////////////////////TODO: how to extract the required flags from CONF?? + typedef ActualInvocationProcess InvocationStateType; + + typedef NodeWiring Product; + + Product * dummy (0); + return *dummy; } }; - typedef ConfigSelector WiringSelector; - + typedef ConfigSelector< WiringDescriptorFactory ///< Factory template + , WiringDescriptor& ///< type of the product + , Alloc& ///< allocator fed to all factories + > WiringSelector; + + struct WiringFactoryImpl { -// WiringSelector selector; + WiringSelector selector; WiringFactoryImpl (Alloc& alloc) -// : selector(PossibleConfigs::List(), alloc) + : selector(config::PossibleConfigs::List(), alloc) { } }; @@ -119,6 +132,6 @@ namespace engine { // return pImpl_->selector(config); } // BlockAlloc > > >::fabricate(); - + } // namespace engine diff --git a/src/proc/engine/nodewiring.hpp b/src/proc/engine/nodewiring.hpp index 2d1e587a4..442ca4297 100644 --- a/src/proc/engine/nodewiring.hpp +++ b/src/proc/engine/nodewiring.hpp @@ -44,8 +44,9 @@ namespace engine { * Actual implementation of the link between nodes, * also acting as "track switch" for the execution path * choosen while operating the node network for rendering. - * @param STATE StateAdapter object controlling the + * @param STATE Invocation state object controlling the * behaviour of callDown() while rendering. + * @see StateAdapter * @see NodeFactory */ template diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index c6df0c0db..0ebefd865 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -99,8 +99,8 @@ namespace engine { * heap allocated. */ template< template class Factory - , typename PAR ///< ctor parameter of the Factories - , typename RET ///< common base class of the Factory's products + , typename RET ///< common base class of the Factory's products + , typename PAR ///< ctor parameter of the Factories > class ConfigSelector { diff --git a/src/proc/engine/procnode.hpp b/src/proc/engine/procnode.hpp index 13d6e2241..343c6a6e8 100644 --- a/src/proc/engine/procnode.hpp +++ b/src/proc/engine/procnode.hpp @@ -133,13 +133,16 @@ namespace engine { static NodeFactory create; ///////TODO: really? probably we'll rather have a NodeFactory object in the builder... /** Engine Core operation: render and pull output from this node. - * On return, currentProcess will hold onto output buffer(s) - * containing the calculated result frames. + * On return, currentProcess will hold onto output buffer(s) + * containing the calculated result frames. In case this node + * calculates a multi channel output, only one channel can be + * retrieved by such a \c pull() call, but you can expect data + * of the other channels to be processed and fed to cache. * @param currentProcess the current processing state for * managing buffers and accessing current parameter values * @param requiredOutputNr the output channel requested (in case * this node delivers more than one output channel) - * @return ID of the result buffer (accessible via currentProcess) + * @return handle to the buffer containing the calculated result. */ BuffHandle pull (State& currentProcess, uint requiredOutputNr=0) const diff --git a/src/proc/mobject/builder/applicablebuildertargettypes.hpp b/src/proc/mobject/builder/applicablebuildertargettypes.hpp index e51fa1251..b02fb9d45 100644 --- a/src/proc/mobject/builder/applicablebuildertargettypes.hpp +++ b/src/proc/mobject/builder/applicablebuildertargettypes.hpp @@ -55,7 +55,7 @@ namespace mobject { * common selection of types to be handled by any such builder tool. * The actual BuilderTool impl should inherit from this template by * feeding back its type (CRTP), this causes a dispatcher table entry - * be generated for this concrete BuilderTool implementation. + * to be generated for this concrete BuilderTool implementation. */ template struct ApplicableBuilderTargetTypes diff --git a/src/proc/mobject/builder/operationpoint.hpp b/src/proc/mobject/builder/operationpoint.hpp index daa0f18d6..f72d9c711 100644 --- a/src/proc/mobject/builder/operationpoint.hpp +++ b/src/proc/mobject/builder/operationpoint.hpp @@ -54,7 +54,7 @@ namespace mobject { public: /** create node(s) corresponding to the given Processor-Asset * and wire them as a successor to this OperationPoint; then - * move this point to point at the resulting new exit node(s) + * move this point to refer to the resulting new exit node(s) */ void attach (asset::PProc const&); diff --git a/tests/50components.tests b/tests/50components.tests index 2bac409cb..9b15ca3ee 100644 --- a/tests/50components.tests +++ b/tests/50components.tests @@ -50,6 +50,109 @@ out: Standard Cmdlineformat:one two END +TEST "ConfigFlags_test" ConfigFlags_test <- +out: Conf2 :-<2>- +out: Conf3 :-<3>- +out: Conf4 :-<2>-<4>- +out: AllFlags :-<1>-<2>-<3>-<4>- +out: ______ +out: ______ check_flags() +out: Flags1 :-<2>-<4>- +out: Flags2 :-<2>-<4>- +out: SimpleConfig_defined_by_Typelist :-<1>- +out: AnotherConfig_defined_by_Typelist :-<1>-<2>-<3>-<4>- +out: ______ +out: ______ check_instantiation() +out: defined Conf0? ---> 0 +out: defined Conf1? ---> 1 +out: defined Conf2? ---> 1 +out: defined Conf3? ---> 1 +out: defined Conf4? ---> 1 +out: defined Trash? ---> 0 +out: ______ +out: ______ check_filter() +out: SomeFlagsets : +out: +---<1>-<3>-+ +out: +---<2>-<4>-+- +out: Configs_defined_by_Flagsets : +out: +-Conf-[-<1>-<3>-] +out: +-Conf-[-<2>-<4>-]- +out: Filter_possible_Configs : +out: +-Conf-[-<2>-<4>-]- +out: AllFlagCombinations : +out: +---<1>-<2>-<3>-<4>-<·>-+ +out: +---<1>-<2>-<3>-<·>-+ +out: +---<1>-<2>-<4>-<·>-+ +out: +---<1>-<2>-<·>-+ +out: +---<1>-<3>-<4>-<·>-+ +out: +---<1>-<3>-<·>-+ +out: +---<1>-<4>-<·>-+ +out: +---<1>-<·>-+ +out: +---<2>-<3>-<4>-<·>-+ +out: +---<2>-<3>-<·>-+ +out: +---<2>-<4>-<·>-+ +out: +---<2>-<·>-+ +out: +---<3>-<4>-<·>-+ +out: +---<3>-<·>-+ +out: +---<4>-<·>-+ +out: +---<·>-+- +out: ListAllConfigs : +out: +-Conf-[-<1>-<2>-<3>-<4>-] +out: +-Conf-[-<1>-<2>-<3>-] +out: +-Conf-[-<1>-<2>-<4>-] +out: +-Conf-[-<1>-<2>-] +out: +-Conf-[-<1>-<3>-<4>-] +out: +-Conf-[-<1>-<3>-] +out: +-Conf-[-<1>-<4>-] +out: +-Conf-[-<1>-] +out: +-Conf-[-<2>-<3>-<4>-] +out: +-Conf-[-<2>-<3>-] +out: +-Conf-[-<2>-<4>-] +out: +-Conf-[-<2>-] +out: +-Conf-[-<3>-<4>-] +out: +-Conf-[-<3>-] +out: +-Conf-[-<4>-] +out: +-Conf-[-]- +out: Filter_all_possible_Configs : +out: +-Conf-[-<1>-] +out: +-Conf-[-<2>-<3>-] +out: +-Conf-[-<2>-<4>-] +out: +-Conf-[-<2>-] +out: +-Conf-[-<3>-]- +out: ______ +out: ______ check_FlagInfo() +out: Flags1 :-<1>-<3>- +out: max bit : 3 +out: binary code: 10 +out: SomeConfigs : +out: +-Conf-[-<1>-<3>-] +out: +-Conf-[-<2>-<4>-]- +out: max bit in [SomeConfigs] : 4 +out: TestVisitor application: +out: visit(code=10) --> +out: +-Conf-[-<1>-<3>-]- +out: visit(code=20) --> +out: +-Conf-[-<2>-<4>-]- +out: ______ +out: ______ check_ConfigSelector() +out: Possible_Configs : +out: +-Conf-[-<1>-] +out: +-Conf-[-<2>-<3>-] +out: +-Conf-[-<2>-<4>-] +out: +-Conf-[-<2>-] +out: +-Conf-[-<3>-]- +out: Flag-code = 2 ConfigSelector() ---> 1010 +out: Flag-code = 12 ConfigSelector() ---> 1023 +out: Flag-code = 20 ConfigSelector() ---> 1024 +out: Flag-code = 4 ConfigSelector() ---> 1020 +out: Flag-code = 8 ConfigSelector() ---> 1030 +out: LUMIERA_ERROR_INVALID:invalid input or parameters (ConfigSelector: No preconfigured factory for config-bits=23). +return: 0 +END + + TEST "CustomSharedPtr_test" CustomSharedPtr_test < > out: ctor DoIt > out: ctor DoIt > @@ -241,6 +344,106 @@ return: 0 END +TEST "TypeListManipl_test" TypeListManipl_test <-<2>-<3>- +out: List2 :-<5>-<6>-<7>- +out: Added2 :-<3>-<4>-<5>- +out: Append1 :- +out: Append2 :-<11>-<22>- +out: Append3 :-<111>- +out: Append4 :-<222>- +out: Append5 :-<1>-<2>-<3>- +out: Append6 :-<5>-<6>-<7>- +out: Append7 :-<111>-<5>-<6>-<7>- +out: Append8 :-<1>-<2>-<3>-<222>- +out: Append9 :-<1>-<2>-<3>-<5>-<6>-<7>- +out: FilterEven :-<2>-<6>- +out: Prefix1 : +out: +---<11>-<22>-+- +out: Prefix2 : +out: +---<101>-<1>-+ +out: +---<101>-<2>-+ +out: +---<101>-<3>-+- +out: Prefix3 : +out: +---<1>-+ +out: +---<2>-+ +out: +---<3>-+- +out: Prefix4 : +out: +---<111>-<1>-<2>-<3>-+ +out: +---<111>-<0>-+ +out: +---<111>-<5>-<6>-<7>-+- +out: Prefix5 : +out: +---<1>-<2>-<3>-<5>-+ +out: +---<1>-<2>-<3>-<6>-+ +out: +---<1>-<2>-<3>-<7>-+- +out: Prefix6 : +out: +---<1>-<2>-<3>-<1>-<2>-<3>-+ +out: +---<1>-<2>-<3>-<0>-+ +out: +---<1>-<2>-<3>-<5>-<6>-<7>-+- +out: Dist1 : +out: +---<11>-<1>-+ +out: +---<11>-<2>-+ +out: +---<11>-<3>-+- +out: Dist2 : +out: +---<11>-<0>-+ +out: +---<22>-<0>-+ +out: +---<33>-<0>-+- +out: Dist3 : +out: +---<11>-<1>-+ +out: +---<11>-<2>-+ +out: +---<11>-<3>-+ +out: +---<22>-<1>-+ +out: +---<22>-<2>-+ +out: +---<22>-<3>-+ +out: +---<33>-<1>-+ +out: +---<33>-<2>-+ +out: +---<33>-<3>-+- +out: Dist4 : +out: +---<11>-<1>-<2>-<3>-+ +out: +---<11>-<5>-<6>-<7>-+ +out: +---<22>-<1>-<2>-<3>-+ +out: +---<22>-<5>-<6>-<7>-+ +out: +---<33>-<1>-<2>-<3>-+ +out: +---<33>-<5>-<6>-<7>-+- +out: Down :-<11>-<10>-<9>-<8>-<7>-<6>-<5>-<4>-<3>-<2>-<1>-<0>- +out: Combi : +out: +---<1>-<2>-<3>-<·>-+ +out: +---<1>-<2>-<2>-<·>-+ +out: +---<1>-<2>-<1>-<·>-+ +out: +---<1>-<2>-<0>-<·>-+ +out: +---<1>-<1>-<3>-<·>-+ +out: +---<1>-<1>-<2>-<·>-+ +out: +---<1>-<1>-<1>-<·>-+ +out: +---<1>-<1>-<0>-<·>-+ +out: +---<1>-<0>-<3>-<·>-+ +out: +---<1>-<0>-<2>-<·>-+ +out: +---<1>-<0>-<1>-<·>-+ +out: +---<1>-<0>-<0>-<·>-+ +out: +---<0>-<2>-<3>-<·>-+ +out: +---<0>-<2>-<2>-<·>-+ +out: +---<0>-<2>-<1>-<·>-+ +out: +---<0>-<2>-<0>-<·>-+ +out: +---<0>-<1>-<3>-<·>-+ +out: +---<0>-<1>-<2>-<·>-+ +out: +---<0>-<1>-<1>-<·>-+ +out: +---<0>-<1>-<0>-<·>-+ +out: +---<0>-<0>-<3>-<·>-+ +out: +---<0>-<0>-<2>-<·>-+ +out: +---<0>-<0>-<1>-<·>-+ +out: +---<0>-<0>-<0>-<·>-+- +out: OnOff : +out: +---<1>-<2>-<3>-<·>-+ +out: +---<1>-<2>-<·>-+ +out: +---<1>-<3>-<·>-+ +out: +---<1>-<·>-+ +out: +---<2>-<3>-<·>-+ +out: +---<2>-<·>-+ +out: +---<3>-<·>-+ +out: +---<·>-+- +return: 0 +END + + TEST "VisitingTool_test" VisitingTool_test < Flags1; typedef Flags Flags2; @@ -186,7 +186,8 @@ namespace lumiera { << "? ---> " \ << Instantiation::Test::value << "\n"; - cout << "\n==== check_instantiation()\n"; + DELIMITER (check_instantiation()); + CAN_INSTANTIATE (Conf0); CAN_INSTANTIATE (Conf1); CAN_INSTANTIATE (Conf2); @@ -203,7 +204,7 @@ namespace lumiera { */ void check_filter () { - cout << "\n==== check_filter()\n"; + DELIMITER (check_filter()); DISPLAY (SomeFlagsets); @@ -238,7 +239,7 @@ namespace lumiera { void visit (ulong code) { - result += str (format ("visit(code=%u) --> %s\n") + result += str (format ("visit(code=%u) -->%s\n") % code % Printer::print() ); } }; @@ -249,7 +250,7 @@ namespace lumiera { */ void check_FlagInfo() { - cout << "\n==== check_FlagInfo()\n"; + DELIMITER (check_FlagInfo()); DISPLAY (Flags1); cout << "max bit : " << FlagInfo::BITS <<"\n"; @@ -281,13 +282,16 @@ namespace lumiera { */ void check_ConfigSelector() { - cout << "\n==== check_ConfigSelector()\n"; + DELIMITER (check_ConfigSelector()); typedef Apply ListAllConfigs; typedef Filter::Test> Possible_Configs; DISPLAY (Possible_Configs); - typedef engine::config::ConfigSelector TestFactorySelector; + typedef engine::config::ConfigSelector< TestFactory // Factory template + , uint // product type + , long // common ctor argument + > TestFactorySelector; const long offset = 1000; // parameter fed to all TestFactory ctors TestFactorySelector testConfigSelector (Possible_Configs::List(), offset); diff --git a/tests/common/meta/typelistdiagnostics.hpp b/tests/common/meta/typelistdiagnostics.hpp index bccc05ab2..8de0e8d76 100644 --- a/tests/common/meta/typelistdiagnostics.hpp +++ b/tests/common/meta/typelistdiagnostics.hpp @@ -37,6 +37,7 @@ #include "common/meta/generator.hpp" + //#include "common/meta/typelistutil.hpp" //#include "common/meta/util.hpp" //#include "common/util.hpp" @@ -49,11 +50,33 @@ using boost::format; namespace lumiera { namespace typelist { + + /** constant-wrapper type for debugging purposes, + * usable for generating lists of distinguishable types + */ + template + struct Num + { + enum{ VAL=I }; + }; + + + /* some forwards used by configflagstest.cpp */ + template struct Flag; + template< char f1 + , char f2 + , char f3 + , char f4 + , char f5 + > + struct Config; + + + namespace test { - - namespace { // internal definitions + using boost::format; format fmt ("-<%u>%s"); @@ -75,8 +98,15 @@ namespace lumiera { static string print () { return str( fmt % "·" % BASE::print()); } }; + template + struct Printer, BASE> ///< display the presence of a Num instance in the typelist + : BASE + { + static string print () { return str( fmt % uint(Num::VAL) % BASE::print()); } + }; + template - struct Printer, BASE> + struct Printer, BASE> ///< display the presence of a Flag in the typelist : BASE { static string print () { return str( fmt % uint(Fl) % BASE::print()); } @@ -124,7 +154,7 @@ namespace lumiera { typedef InstantiateChained Contents_##LIST; #define DISPLAY(NAME) \ - DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n"; + DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t:" << Contents_##NAME::print() << "\n"; diff --git a/tests/common/meta/typelistmaniptest.cpp b/tests/common/meta/typelistmaniptest.cpp index 9d2321299..f2b86c11e 100644 --- a/tests/common/meta/typelistmaniptest.cpp +++ b/tests/common/meta/typelistmaniptest.cpp @@ -41,6 +41,7 @@ #include "common/test/run.hpp" #include "common/meta/generator.hpp" #include "common/meta/typelistutil.hpp" +#include "meta/typelistdiagnostics.hpp" #include "common/util.hpp" #include @@ -56,65 +57,7 @@ namespace lumiera { namespace test { - namespace { // internal definitions - - boost::format fmt ("<%i>"); - - /** constant-wrapper type for debugging purposes, - * usable for generating lists of distinguishable types - */ - template - struct Num - { - enum{ VAL=I }; - static string str () { return boost::str (fmt % I); } - }; - - - /** debugging template, - * printing the "number" used for intantiation - */ - template - struct Printer; - - template - struct Printer - { - static string print () { return "-"; } - }; - - template - struct Printer, BASE> - : BASE - { - static string print () { return string("-") + Num::str() + BASE::print(); } - }; - - - typedef Printer NullP; - - /** call the debug-print for a typelist - * utilizing the Printer template */ - template - string - printSublist () - { - typedef InstantiateChained SubList; - return SubList::print(); - } - - /** Spezialisation for debug-printing of a nested sublist */ - template - struct Printer, BASE> - : BASE - { - static string print () - { - typedef Node List; - return string("\n\t+--") + printSublist()+"+" - + BASE::print(); - } - }; + namespace { // test data @@ -131,16 +74,10 @@ namespace lumiera { template struct CountDown { typedef NullType List; }; template<> struct CountDown > { typedef Node, NullType> List; }; template struct CountDown > { typedef Node, typename CountDown >::List> List; }; - -#define DIAGNOSE(LIST) \ - typedef InstantiateChained Contents_##LIST; - -#define DISPLAY(NAME) \ - DIAGNOSE(NAME); cout << STRINGIFY(NAME) << "\t" << Contents_##NAME::print() << "\n"; - } // (End) internal defs + } // (End) test data @@ -174,8 +111,8 @@ namespace lumiera { void check_diagnostics () { // Explanation: the DISPLAY macro expands as follows.... - typedef InstantiateChained > Contents_List1; - cout << "List1" << "\t" << Contents_List1::print() << "\n"; + typedef InstantiateChained Contents_List1; + cout << "List1" << "\t:" << Contents_List1::print() << "\n"; // That is: we instantiate the "Printer" template for each of the types in List1, // forming an inheritance chain. I.e. the defined Type "Contents_List1" inherits From 07975e520f29a15cea73a03a43a4cab2bf30d800 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 5 Aug 2008 03:46:06 +0200 Subject: [PATCH 20/40] change ConfigSelector to accept arbitrary factory function signatures --- src/common/meta/configflags.hpp | 2 ++ src/proc/engine/nodewiring.cpp | 5 ++-- src/proc/engine/nodewiringconfig.hpp | 38 +++++++++++++-------------- tests/50components.tests | 22 ++++++++-------- tests/common/meta/configflagstest.cpp | 19 ++++++++------ 5 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/common/meta/configflags.hpp b/src/common/meta/configflags.hpp index 7ffb79a70..fa24bfc97 100644 --- a/src/common/meta/configflags.hpp +++ b/src/common/meta/configflags.hpp @@ -55,6 +55,8 @@ namespace lumiera { namespace typelist { + const size_t CONFIG_FLAGS_MAX = 5; + template struct Flag { typedef Flag ID; }; template<> struct Flag<0> { typedef NullType ID; }; diff --git a/src/proc/engine/nodewiring.cpp b/src/proc/engine/nodewiring.cpp index 6a20f86e4..7d999aa48 100644 --- a/src/proc/engine/nodewiring.cpp +++ b/src/proc/engine/nodewiring.cpp @@ -89,9 +89,10 @@ namespace engine { } }; + typedef WiringDescriptor& (FunctionType)(void); typedef ConfigSelector< WiringDescriptorFactory ///< Factory template - , WiringDescriptor& ///< type of the product + , FunctionType ///< function signature of the Factory , Alloc& ///< allocator fed to all factories > WiringSelector; @@ -129,7 +130,7 @@ namespace engine { // Bits config (FlagInfo::CODE); size_t config = 13; /////////////////////////////////////////TODO -// return pImpl_->selector(config); +// return pImpl_->selector[config](); } // BlockAlloc > > >::fabricate(); diff --git a/src/proc/engine/nodewiringconfig.hpp b/src/proc/engine/nodewiringconfig.hpp index 0ebefd865..aac1ff7a1 100644 --- a/src/proc/engine/nodewiringconfig.hpp +++ b/src/proc/engine/nodewiringconfig.hpp @@ -51,7 +51,8 @@ #include "common/util.hpp" #include "common/meta/configflags.hpp" -#include +#include +#include #include @@ -59,10 +60,10 @@ namespace engine { namespace config { using lumiera::typelist::FlagInfo; - - using boost::lexical_cast; using util::contains; + using lumiera::typelist::CONFIG_FLAGS_MAX; + /** * Helper for fabricating ProcNode Wiring configurations. @@ -99,28 +100,26 @@ namespace engine { * heap allocated. */ template< template class Factory - , typename RET ///< common base class of the Factory's products + , typename FUNC ///< common function type of all Factory instances , typename PAR ///< ctor parameter of the Factories > class ConfigSelector { + typedef boost::function FacFunction; - struct FacFunctor - { - virtual ~FacFunctor() {} - virtual RET invoke() =0; - }; template - struct FactoryHolder : FacFunctor + struct FactoryHolder + : private FAC, + public FacFunction { - FAC factory_; - FactoryHolder(PAR p) : factory_(p) {} - - virtual RET invoke () { return factory_(); } + FactoryHolder(PAR p) + : FAC(p), + FacFunction( static_cast(*this)) + { } }; - typedef std::tr1::shared_ptr PFunc; + typedef std::tr1::shared_ptr PFunc; typedef std::map ConfigTable; ConfigTable possibleConfig_; ///< Table of factories @@ -162,13 +161,14 @@ namespace engine { FlagInfo::accept (buildTable); } - RET - operator() (size_t configFlags) ///< invoke the factory corresponding to the given config + FacFunction& + operator[] (size_t configFlags) ///< retrieve the factory corresponding to the given config { if (contains (possibleConfig_, configFlags)) - return possibleConfig_[configFlags]->invoke(); + return *possibleConfig_[configFlags]; else - throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits="+lexical_cast(configFlags)); + throw lumiera::error::Invalid("ConfigSelector: No preconfigured factory for config-bits=" + +std::bitset(configFlags).to_string()); } }; diff --git a/tests/50components.tests b/tests/50components.tests index 9b15ca3ee..90ab37569 100644 --- a/tests/50components.tests +++ b/tests/50components.tests @@ -57,22 +57,22 @@ out: Conf2 :-<2>- out: Conf3 :-<3>- out: Conf4 :-<2>-<4>- out: AllFlags :-<1>-<2>-<3>-<4>- -out: ______ -out: ______ check_flags() +out: __________________________ +out: __________________________ check_flags() out: Flags1 :-<2>-<4>- out: Flags2 :-<2>-<4>- out: SimpleConfig_defined_by_Typelist :-<1>- out: AnotherConfig_defined_by_Typelist :-<1>-<2>-<3>-<4>- -out: ______ -out: ______ check_instantiation() +out: __________________________ +out: __________________________ check_instantiation() out: defined Conf0? ---> 0 out: defined Conf1? ---> 1 out: defined Conf2? ---> 1 out: defined Conf3? ---> 1 out: defined Conf4? ---> 1 out: defined Trash? ---> 0 -out: ______ -out: ______ check_filter() +out: __________________________ +out: __________________________ check_filter() out: SomeFlagsets : out: +---<1>-<3>-+ out: +---<2>-<4>-+- @@ -121,8 +121,8 @@ out: +-Conf-[-<2>-<3>-] out: +-Conf-[-<2>-<4>-] out: +-Conf-[-<2>-] out: +-Conf-[-<3>-]- -out: ______ -out: ______ check_FlagInfo() +out: __________________________ +out: __________________________ check_FlagInfo() out: Flags1 :-<1>-<3>- out: max bit : 3 out: binary code: 10 @@ -135,8 +135,8 @@ out: visit(code=10) --> out: +-Conf-[-<1>-<3>-]- out: visit(code=20) --> out: +-Conf-[-<2>-<4>-]- -out: ______ -out: ______ check_ConfigSelector() +out: __________________________ +out: __________________________ check_ConfigSelector() out: Possible_Configs : out: +-Conf-[-<1>-] out: +-Conf-[-<2>-<3>-] @@ -148,7 +148,7 @@ out: Flag-code = 12 ConfigSelector() ---> 1023 out: Flag-code = 20 ConfigSelector() ---> 1024 out: Flag-code = 4 ConfigSelector() ---> 1020 out: Flag-code = 8 ConfigSelector() ---> 1030 -out: LUMIERA_ERROR_INVALID:invalid input or parameters (ConfigSelector: No preconfigured factory for config-bits=23). +out: LUMIERA_ERROR_INVALID:invalid input or parameters (ConfigSelector: No preconfigured factory for config-bits=10111). return: 0 END diff --git a/tests/common/meta/configflagstest.cpp b/tests/common/meta/configflagstest.cpp index 1d1bad7db..34b54be48 100644 --- a/tests/common/meta/configflagstest.cpp +++ b/tests/common/meta/configflagstest.cpp @@ -112,7 +112,10 @@ namespace lumiera { } // (End) internal defs -#define DELIMITER(TITLE) cout << "______\n______ " << STRINGIFY(TITLE) << "\n"; +#define PRINT_DELIMITER(TITLE) \ + cout << "__________________________\n" \ + "__________________________ " \ + << STRINGIFY(TITLE) << "\n"; @@ -155,7 +158,7 @@ namespace lumiera { /** @test conversion between list-of-flags and a config-type in both directions */ void check_flags () { - DELIMITER (check_flags()); + PRINT_DELIMITER (check_flags()); typedef Config Flags1; typedef Flags Flags2; @@ -186,7 +189,7 @@ namespace lumiera { << "? ---> " \ << Instantiation::Test::value << "\n"; - DELIMITER (check_instantiation()); + PRINT_DELIMITER (check_instantiation()); CAN_INSTANTIATE (Conf0); CAN_INSTANTIATE (Conf1); @@ -204,7 +207,7 @@ namespace lumiera { */ void check_filter () { - DELIMITER (check_filter()); + PRINT_DELIMITER (check_filter()); DISPLAY (SomeFlagsets); @@ -250,7 +253,7 @@ namespace lumiera { */ void check_FlagInfo() { - DELIMITER (check_FlagInfo()); + PRINT_DELIMITER (check_FlagInfo()); DISPLAY (Flags1); cout << "max bit : " << FlagInfo::BITS <<"\n"; @@ -282,14 +285,14 @@ namespace lumiera { */ void check_ConfigSelector() { - DELIMITER (check_ConfigSelector()); + PRINT_DELIMITER (check_ConfigSelector()); typedef Apply ListAllConfigs; typedef Filter::Test> Possible_Configs; DISPLAY (Possible_Configs); typedef engine::config::ConfigSelector< TestFactory // Factory template - , uint // product type + , uint(void) // Factory function type , long // common ctor argument > TestFactorySelector; @@ -300,7 +303,7 @@ namespace lumiera { #define INVOKE_CONFIG_SELECTOR(CODE) \ cout << " Flag-code = " << CODE \ << " ConfigSelector() ---> " \ - << testConfigSelector (CODE) << "\n"; + << testConfigSelector[CODE] () << "\n"; INVOKE_CONFIG_SELECTOR (2); INVOKE_CONFIG_SELECTOR (12); From 8803d86e27cb35b6dcc3d18518d160ba2c53c43e Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 6 Aug 2008 05:29:29 +0200 Subject: [PATCH 21/40] start planning towards how to describe the type of frames and buffers --- wiki/renderengine.html | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index c55d698a2..9b194b09a 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1279,7 +1279,7 @@ For this Lumiera design, we could consider making GOP just another raw media dat &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
    -
    +
    This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Lumiera Renderengine, the Builder and the related parts.
     
     * [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
    @@ -1296,6 +1296,8 @@ For this Lumiera design, we could consider making GOP just another raw media dat
     * [[integrating the Config Query system|ConfigQueryIntegration]]
     * [[identifying the basic Builder operations|BasicBuildingOperations]] and [[planning the Implementation|PlanningNodeCreatorTool]]
     * [[how to handle »attached placement«|AttachedPlacementProblem]]
    +* working out the [[basic building situations|BuilderPrimitives]] and [[mechanics of rendering|RenderMechanics]]
    +* how to classify and [[describe media stream types|StreamTypeDescr]]
     
    @@ -3066,6 +3068,32 @@ if (oldText.indexOf("SplashScreen")==-1) * moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
    +
    +
    //how to classify and describe media streams//
    +Media data is understood to appear structured as stream(s) over time. While there may be an inherent internal structuring, at a given perspective ''any stream is a unit and homogeneous''. In the context of digital media data processing, streams are always ''quantized'', which means they appear as a temporal sequence of data chunks called ''frames''.
    +
    +! Terminology
    +* __Media__ is comprised of a set of streams or channels
    +* __Stream__ denotes a homogeneous flow of media data of a single kind
    +* __Channel__ denotes a elementary stream, which can't be further separated in the given context
    +* all of these are delivered and processed in a smallest unit called __Frame__. Each frame corresponds to a //time interval.//
    +* a __Buffer__ is a data structure capable of holding a Frame of media data.
    +* the __~Stream-Type__ describes the kind of media data contained in the stream
    +
    +! Problem of Stream Type Description
    +Media types vary largely and exhibit a large number of different properties, which can't be subsumed under a single classification scheme. But on the other hand, we want to deal with media objects in a uniform and generic manner, because on the large all kinds of media behave somewhat similar. The problem is, these similarities disappear when describing media with logical precision. Thus we are forced into specialized handling and operations for each kind of media, while we want to implement a generic handling concept.
    +
    +! Handling Stream Type Description
    +!! Identification
    +A stream type is denoted by a ~StreamID, which is an identifier, acting as an unique key for accessing information related to the stream type.
    +
    +!! Requirements
    +* set up a buffer and be able to create/retrieve frames of media data.
    +* determine if sources and sinks for media data can be connected, and how.
    +* determine and enumerate the internal structure of a stream.
    +* discover processing facilities
    +
    +
    This design lays great emphasis on separating all those components and subsystems, which are considered not to have a //natural link// of their underlying concepts. This often means putting some additional constraints on the implementation, so basically we need to rely on the actual implementation to live up to this goal. In many cases it may seem to be more natural to "just access the necessary information". But on the long run this coupling of not-directly related components makes the whole codebase monolithic and introduces lots of //accidental complexity.//
     
    
    From 2625067e378d74330dffb477c4e7ebd62529049d Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Sat, 9 Aug 2008 08:15:37 +0200
    Subject: [PATCH 22/40] started a drawing showing the high level model
     structure.
    
    ---
     doc/devel/draw/high-level-model.svg | 1307 +++++++++++++++++++++++++++
     1 file changed, 1307 insertions(+)
     create mode 100644 doc/devel/draw/high-level-model.svg
    
    diff --git a/doc/devel/draw/high-level-model.svg b/doc/devel/draw/high-level-model.svg
    new file mode 100644
    index 000000000..7659ba7f2
    --- /dev/null
    +++ b/doc/devel/draw/high-level-model.svg
    @@ -0,0 +1,1307 @@
    +
    +
    +
    +  
    +  
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +      
    +      
    +      
    +    
    +    
    +      
    +      
    +      
    +    
    +    
    +    
    +      
    +    
    +    
    +      
    +      
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +    
    +      
    +    
    +  
    +  
    +    
    +      
    +        image/svg+xml
    +        
    +        
    +        Lumiera: Anatomy of the high-level-model
    +        
    +          
    +            Ichthyostega
    +          
    +        
    +        8/2008
    +      
    +      
    +        
    +        
    +        
    +        
    +        
    +        
    +      
    +    
    +  
    +  
    +    
    +      
    +      
    +      
    +      
    +        
    +        
    +        
    +      
    +    
    +    
    +      
    +        
    +        
    +      
    +      ∑
    +    
    +    
    +      
    +        
    +        
    +        
    +          
    +            
    +              
    +              
    +            
    +            
    +              
    +                
    +                
    +              
    +              Proc
    +            
    +          
    +          
    +            
    +              
    +              
    +                
    +                x
    +              
    +            
    +            
    +          
    +          
    +            
    +              
    +                
    +                
    +              
    +              
    +            
    +            
    +            Tap
    +          
    +          
    +          
    +            
    +          
    +        
    +        
    +      
    +      
    +        
    +        
    +      
    +      
    +        
    +        
    +          
    +            
    +            
    +          
    +          W
    +        
    +      
    +    
    +    
    +      
    +        
    +        
    +      
    +      
    +    
    +    
    +      
    +        
    +        
    +      
    +      
    +        
    +        
    +      
    +      
    +        
    +        
    +      
    +      
    +        
    +          
    +          
    +        
    +        Transition
    +      
    +    
    +    
    +      
    +      
    +        
    +        
    +      
    +      
    +      
    +    
    +    
    +    
    +      
    +        
    +        
    +          
    +          
    +        
    +        
    +      
    +      Clip
    +    
    +    
    +      
    +      
    +        
    +        
    +        
    +          
    +          
    +          
    +        
    +        W
    +        t
    +      
    +      
    +        
    +        
    +        
    +      
    +    
    +    
    +      
    +        
    +        
    +          
    +          
    +        
    +      
    +      Auto
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +      
    +      
    +    
    +    
    +    
    +  
    +  
    +    Anatomy of thehigh-level model
    +    The model is an association of Media Objects,grouped to form Processing Pipes, which area linear chain of data processing modules,starting from an source port and providingan exit point.Optionally, there may be a wiring plug, requestingthe exit point to be connected to another pipe. Whenomitted, the wiring will be figured out automatically.Several pipes may be joined together by a transition,which in the general case simultanously  treats Nmedia streams. Commonly, a transition simply combines two streams into one output.
    +    Individual Media Objects are attached,located or joined together by Placements.A Placement is a handle for a single MObjectand contains a list of placement specifications,(called Locating Pin),Besides absolute and relative placement, thereis also the possibility of a placement to stick directly to another MObject's placement, e.g.to attach an effect to a clip or an automationdata set to an effect.
    +  
    +
    
    From 7fd70a476cfb468d99c72f08995c7ca7e94e376a Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Sat, 9 Aug 2008 19:42:22 +0200
    Subject: [PATCH 23/40] add a description and my favorite Lumiera Logo
    
    ---
     doc/devel/draw/high-level-model.svg | 370 +++++++++++++++-------------
     1 file changed, 194 insertions(+), 176 deletions(-)
    
    diff --git a/doc/devel/draw/high-level-model.svg b/doc/devel/draw/high-level-model.svg
    index 7659ba7f2..f7f5f374b 100644
    --- a/doc/devel/draw/high-level-model.svg
    +++ b/doc/devel/draw/high-level-model.svg
    @@ -10,7 +10,7 @@
        xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
        xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
        width="744.09448"
    -   height="1052.3622"
    +   height="1052.36"
        id="svg2"
        sodipodi:version="0.32"
        inkscape:version="0.45.1"
    @@ -25,86 +25,99 @@
          borderopacity="1.0"
          inkscape:pageopacity="0.0"
          inkscape:pageshadow="2"
    -     inkscape:zoom="1"
    -     inkscape:cx="271.21781"
    -     inkscape:cy="877.33161"
    +     inkscape:zoom="0.78110154"
    +     inkscape:cx="372.04724"
    +     inkscape:cy="530.28803"
          inkscape:document-units="px"
    -     inkscape:current-layer="layer1"
    +     inkscape:current-layer="lay_Label"
          inkscape:window-width="1352"
          inkscape:window-height="1016"
    -     inkscape:window-x="50"
    +     inkscape:window-x="0"
          inkscape:window-y="0"
    -     width="760px"
    -     height="1024px"
    -     showgrid="true"
    +     width="744.09448px"
    +     height="1052.36px"
    +     showgrid="false"
          gridspacingx="1px"
          gridspacingy="1px"
          gridanglex="30px"
          gridanglez="30px"
          grid_units="px"
    -     inkscape:grid-points="false"
    +     inkscape:grid-points="true"
          inkscape:grid-bbox="false"
          gridcolor="#4b4bf4"
          gridopacity="0.09019608"
          showguides="true"
    -     inkscape:guide-bbox="true"
    -     inkscape:object-nodes="true"
    +     inkscape:guide-bbox="false"
    +     inkscape:object-nodes="false"
          gridoriginx="0px"
          gridtolerance="10000"
    -     inkscape:object-paths="false"
    +     inkscape:object-paths="true"
          objecttolerance="1"
    -     inkscape:object-points="false" />
    +     inkscape:object-points="false"
    +     inkscape:guide-points="true"
    +     guidetolerance="5" />
       
    +    
    +      
    +      
    +    
         
    +       refX="0">
           
         
         
    +       refX="0">
           
         
         
    +       refX="0">
           
         
         
    +       refX="0">
           
         
    @@ -112,13 +125,13 @@
            style="overflow:visible"
            orient="auto"
            id="Dot_m"
    -       refY="0.0"
    +       refY="0"
            inkscape:stockid="Dot_m"
    -       refX="0.0">
    +       refX="0">
           
         
         
           
    +       refX="0">
           
         
         
    +       refX="0">
           
         
    +    
    +    
    +    
    +    
    +    
    +    
       
       
    @@ -405,8 +477,7 @@
          inkscape:label="Basics"
          style="display:inline"
          id="lay_Basics"
    -     inkscape:groupmode="layer"
    -     sodipodi:insensitive="true">
    +     inkscape:groupmode="layer">
         
    @@ -1164,144 +1235,91 @@
       
       
    +     id="lay_Label"
    +     inkscape:label="Label">
    +    
    +    
         Anatomy of thehigh-level model
    -    The model is an association of Media Objects,grouped to form Processing Pipes, which area linear chain of data processing modules,starting from an source port and providingan exit point.Optionally, there may be a wiring plug, requestingthe exit point to be connected to another pipe. Whenomitted, the wiring will be figured out automatically.Several pipes may be joined together by a transition,which in the general case simultanously  treats Nmedia streams. Commonly, a transition simply combines two streams into one output.
    -    Individual Media Objects are attached,located or joined together by Placements.A Placement is a handle for a single MObjectand contains a list of placement specifications,(called Locating Pin),Besides absolute and relative placement, thereis also the possibility of a placement to stick directly to another MObject's placement, e.g.to attach an effect to a clip or an automationdata set to an effect.
    +       id="flowRoot3975">The model is an association of Media Objects, grouped to form Processing Pipes, which are a linear chain of data processing modules, starting from an source port and providing an exit point. Optionally, there may be a wiring plug, requesting the exit point to be connected to another pipe. When omitted, the wiring will be figured out automatically.  Several pipes may be joined together by a transition, which in the general case simultanously  treats N media streams. Commonly, a transition simply  combines two streams into one output. Individual Media Objects are attached, located or joined together by Placements. A Placement is a handle for a single MObject and contains a list of placement specifications, (called Locating Pin)Besides absolute and relative placement, there is also the possibility of a placement to stick directly to another MObject's placement, e.g. to attach an effect to a clip or an automation data set to an effect.    
       
     
    
    From 7fd5b673db1eccf8fe3198cabbd1fa27996fb94a Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Sun, 10 Aug 2008 18:36:48 +0200
    Subject: [PATCH 24/40] Draft: fundamental decisions about media stream types
    
    ---
     wiki/renderengine.html | 11 +++++++++--
     1 file changed, 9 insertions(+), 2 deletions(-)
    
    diff --git a/wiki/renderengine.html b/wiki/renderengine.html
    index 9b194b09a..d0227f636 100644
    --- a/wiki/renderengine.html
    +++ b/wiki/renderengine.html
    @@ -3068,7 +3068,7 @@ if (oldText.indexOf("SplashScreen")==-1)
     * moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
     
    -
    +
    //how to classify and describe media streams//
     Media data is understood to appear structured as stream(s) over time. While there may be an inherent internal structuring, at a given perspective ''any stream is a unit and homogeneous''. In the context of digital media data processing, streams are always ''quantized'', which means they appear as a temporal sequence of data chunks called ''frames''.
     
    @@ -3083,10 +3083,17 @@ Media data is understood to appear structured as stream(s) over time. While ther
     ! Problem of Stream Type Description
     Media types vary largely and exhibit a large number of different properties, which can't be subsumed under a single classification scheme. But on the other hand, we want to deal with media objects in a uniform and generic manner, because on the large all kinds of media behave somewhat similar. The problem is, these similarities disappear when describing media with logical precision. Thus we are forced into specialized handling and operations for each kind of media, while we want to implement a generic handling concept.
     
    -! Handling Stream Type Description
    +! Stream Type handling in the Proc-Layer
     !! Identification
     A stream type is denoted by a ~StreamID, which is an identifier, acting as an unique key for accessing information related to the stream type.
     
    +!! Classification
    +Within the Proc-Layer, media streams are treated on the large in a similar manner. But, looking closer, note everyting can be coneected together, wile there are also some classes of media streams which can be considered //equivalent// in most respects. Thus, it seems reasonable to separate the distinction between various media streams into several levels (actually three levels).
    +* Each media belongs to a fundamental ''kind'' of media, examples being __Video__, __Image__, __Audio__, __MIDI__,... Media streams of different kind can be considered somewhat "completely separate" &mdash; just the handling of each media kind follows a common //generic pattern// augmented with specialisations. It is //impossible to connect// media streams of different kind. Under some circumstances there may be the possibility of a transformation though. For example, a still image can be incorporated into video, sound may be visualized, MIDI may control a souond synthesizer.
    +* Below the level of distinct kinds of media streams, we have within every kind a open selection of ''prototypes'', wich may each be "a different thing", but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video which lacks the depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating systems (5.1, 7.1,...), we have common stereponic audio and monaural audio. It is considered important to retain some openness and configuability within this destinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable in some production to distinguish between digitized film and video or NTSC and PAL, while in another production everything is just "video" and can be converted mostly autmatically. The most notable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype.
    +* Besides the distinction in prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchical to the prototype classification, while in detail there will be mostly some sort of dependency. For example, both stereophonig and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, but similarily we may have a dedicated stereo audio stream with the two channels muliplexed into one stream. For dealing with media streams of various implementation type, we need library routines, which also yield a type classification system. Most notably, for raw sound and video data we use the GAVL library, which defines a classification system for buffers and streams.
    +* Besides this type classification, we introduce an ''intention tag''. This is a syntetic classification owned by Lumiera and used for internal wiring decisions. Curently (8/08), we recognize the following intention tags: __Source__, __Raw__, __Intermediary__, __Target__. Only media streams tagget as __Raw__ can be processed.
    +
     !! Requirements
     * set up a buffer and be able to create/retrieve frames of media data.
     * determine if sources and sinks for media data can be connected, and how.
    
    From 302aa087571da0541ca1cfbe662ba7909189c740 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Tue, 12 Aug 2008 04:07:58 +0200
    Subject: [PATCH 25/40] add to the drawing an example featuring a clip with
     attached effects
    
    ---
     doc/devel/draw/high-level-model.svg | 1118 +++++++++++++++++++++------
     wiki/renderengine.html              |   72 +-
     2 files changed, 923 insertions(+), 267 deletions(-)
    
    diff --git a/doc/devel/draw/high-level-model.svg b/doc/devel/draw/high-level-model.svg
    index f7f5f374b..c83f45153 100644
    --- a/doc/devel/draw/high-level-model.svg
    +++ b/doc/devel/draw/high-level-model.svg
    @@ -25,11 +25,11 @@
          borderopacity="1.0"
          inkscape:pageopacity="0.0"
          inkscape:pageshadow="2"
    -     inkscape:zoom="0.78110154"
    -     inkscape:cx="372.04724"
    -     inkscape:cy="530.28803"
    +     inkscape:zoom="1"
    +     inkscape:cx="326.94415"
    +     inkscape:cy="612.06042"
          inkscape:document-units="px"
    -     inkscape:current-layer="lay_Label"
    +     inkscape:current-layer="lay_Basics"
          inkscape:window-width="1352"
          inkscape:window-height="1016"
          inkscape:window-x="0"
    @@ -58,81 +58,138 @@
          guidetolerance="5" />
       
    +    
    +      
    +      
    +      
    +    
    +    
    +      
    +      
    +      
    +      
    +    
    +    
    +      
    +      
    +      
    +      
    +      
    +    
         
           
           
         
         
           
    +         transform="scale(0.4,0.4)" />
         
         
           
    +         transform="scale(0.2,0.2)" />
         
         
           
    +         transform="scale(0.4,0.4)" />
         
         
           
    +         transform="scale(0.4,0.4)" />
         
         
           
    +         transform="matrix(0.4,0,0,0.4,2.96,0.4)" />
         
         
    @@ -180,16 +237,16 @@
            xlink:href="#linearGrad_RoundHigh1" />
         
           
    +         transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
         
         
    @@ -208,42 +265,42 @@
         
         
           
    +         id="path4436" />
         
         
           
    +         transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
         
         
           
    +         transform="scale(-0.6,-0.6)" />
         
         
         
           
    +         transform="matrix(-0.4,0,0,-0.4,-4,0)" />
         
         
         
           
    +         transform="matrix(-0.4,0,0,-0.4,-4,0)" />
         
         
           
    +         id="pathPfeil1" />
         
         
         
           
    +         transform="scale(0.2,0.2)" />
         
         
           
    +         transform="scale(0.4,0.4)" />
         
         
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +      
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +    
    +    
         
    -    
    +    
    -    
    -    
    -    
    +       x="-0.12121263"
    +       width="1.2424253"
    +       y="-0.39648971"
    +       height="1.7929794"
    +       id="filter4646">
    +      
    +    
       
       
    @@ -477,60 +707,11 @@
          inkscape:label="Basics"
          style="display:inline"
          id="lay_Basics"
    -     inkscape:groupmode="layer">
    -    
    -      
    -      
    -      
    -      
    -        
    -        
    -        
    -      
    -    
    +     inkscape:groupmode="layer"
    +     sodipodi:insensitive="true">
         
    +       transform="translate(247.38739,-74.214794)">
           
             
             
    +           d="M 599.42598,26.398361 C 607.73314,26.398361 618.45986,33.956938 618.45986,42.42032 L 614.42086,156.31908 C 614.42086,164.78247 607.73314,171.59595 599.42598,171.59595 C 591.11881,171.59595 584.43109,164.78247 584.43109,156.31908 L 584.43109,41.675225 C 584.43109,33.211843 591.11881,26.398361 599.42598,26.398361 z " />
             
             
    +           d="M 112,212.36359 C 112.4375,208.36359 109.98267,204.77454 120,204.77454 L 140,204.92262 L 160,202.36358" />
             
             
    +           d="M 665,62.362186 C 665,62.362186 665,60.362186 665,57.362186 C 665,54.362186 665.01733,49.773145 655,49.773145 L 635,49.921217 L 615,47.362183" />
             
               
                 
    +               sodipodi:nodetypes="ccccc" />
                 
    +               id="rect_pinBod" />
               
               
           
         
         
           
    +         d="M 150,154.36219 L 150,140.36218" />
           
    @@ -1005,6 +1186,13 @@
                  y="245.67467"
                  x="28.19619"
                  height="9.6891661" />
    +          
             
             
           
    +         id="path_pla2obj" />
           
             
             
    +           d="M 205,289.78 L 225,289.78 L 245,287.36218" />
             
               
             
    +           id="path_Placement_glyph" />
           
         
         
    +       id="path4086"
    +       d="M 570.00119,150.39056 L 652.00089,150.39056 M 570.00119,150.39056 L 570.00119,141.35575 M 570.00119,150.39056 C 570.27729,150.39056 570.55338,150.39056 570.82947,150.39055 C 571.10557,150.39053 571.38166,150.39049 571.65775,150.39032 C 571.93385,150.39016 572.20995,150.38989 572.48603,150.38936 C 572.76213,150.38883 573.03822,150.38803 573.31431,150.38675 C 573.5904,150.38548 573.8665,150.38375 574.14259,150.38129 C 574.41867,150.37882 574.69477,150.3756 574.97087,150.37132 C 575.24696,150.36705 575.52306,150.36171 575.79915,150.35494 C 576.07524,150.34817 576.35133,150.33995 576.62742,150.32985 C 576.90352,150.31977 577.17963,150.3078 577.45571,150.29346 C 577.7318,150.27913 578.00789,150.26244 578.28398,150.24285 C 578.56008,150.22327 578.83618,150.20077 579.11228,150.17485 C 579.38837,150.14891 579.66445,150.11951 579.94055,150.08606 C 580.21663,150.05263 580.49274,150.01512 580.76884,149.97294 C 581.04493,149.9308 581.32101,149.88398 581.59711,149.8319 C 581.8732,149.77982 582.1493,149.72246 582.42539,149.65929 C 582.70149,149.5961 582.97759,149.52706 583.25366,149.45165 C 583.52976,149.37623 583.80587,149.29444 584.08195,149.20581 C 584.35805,149.11719 584.63414,149.02171 584.91023,148.91907 C 585.18632,148.8164 585.46241,148.70655 585.73852,148.58931 C 586.01461,148.47207 586.2907,148.34741 586.5668,148.21535 C 586.84288,148.08328 587.11897,147.94377 587.39508,147.79705 C 587.67117,147.65032 587.94726,147.49635 588.22336,147.33559 C 588.49944,147.17484 588.77554,147.00731 589.05163,146.83374 C 589.32772,146.6602 589.60382,146.4806 589.8799,146.29607 C 590.15599,146.11155 590.43209,145.92208 590.70819,145.7291 C 590.98428,145.53614 591.26038,145.33967 591.53647,145.14154 C 591.81255,144.94339 592.08864,144.74358 592.36475,144.5443 C 592.64085,144.34501 592.91693,144.14625 593.19303,143.95058 C 593.46912,143.7549 593.7452,143.56232 594.02131,143.37569 C 594.2974,143.18906 594.5735,143.00844 594.84959,142.83689 C 595.12568,142.66533 595.40177,142.50291 595.67787,142.35289 C 595.95396,142.20283 596.23006,142.06525 596.50615,141.94335 C 596.78224,141.82145 597.05833,141.71537 597.33443,141.62819 C 597.61053,141.541 597.88662,141.47285 598.16271,141.42654 C 598.43881,141.38021 598.71489,141.35585 598.99099,141.35575 C 599.26709,141.35563 599.54318,141.37994 599.81927,141.43014 C 600.09537,141.48036 600.37146,141.55669 600.64754,141.65972 C 600.92364,141.76274 601.19974,141.8926 601.47584,142.0488 C 601.75192,142.20496 602.02802,142.38757 602.30412,142.59477 C 602.5802,142.80195 602.8563,143.03385 603.13239,143.28712 C 603.40849,143.54038 603.68458,143.81514 603.96067,144.10653 C 604.23677,144.39794 604.51286,144.70606 604.78894,145.02463 C 605.06504,145.34321 605.34113,145.67225 605.61722,146.00418 C 605.89333,146.33611 606.16941,146.67092 606.4455,146.99999 C 606.7216,147.32904 606.99769,147.6523 607.27379,147.96051 C 607.54989,148.26871 607.82597,148.56176 608.10207,148.83032 C 608.37815,149.0989 608.65425,149.34282 608.93035,149.55334 C 609.20645,149.76387 609.48254,149.94076 609.75863,150.07659 C 610.03472,150.21241 610.31081,150.30687 610.5869,150.35459 C 610.86301,150.4023 611.13911,150.40293 611.41518,150.35384 C 611.69128,150.30477 611.96738,150.20566 612.24346,150.05721 C 612.51957,149.90877 612.79566,149.7107 613.07176,149.46749 C 613.34784,149.22428 613.62393,148.93569 613.90003,148.61009 C 614.17613,148.28449 614.45222,147.92174 614.72831,147.53394 C 615.0044,147.14614 615.28049,146.73322 615.55659,146.31049 C 615.83269,145.88774 616.10878,145.4552 616.38487,145.03031 C 616.66096,144.60542 616.93706,144.18836 617.21314,143.79737 C 617.48924,143.40637 617.76535,143.04174 618.04143,142.72086 C 618.31752,142.39998 618.59362,142.12325 618.86971,141.90538 C 619.1458,141.6875 619.4219,141.52894 619.698,141.43986 C 619.97408,141.35079 620.25017,141.33171 620.52626,141.38664 C 620.80236,141.44154 621.07845,141.57094 621.35455,141.77153 C 621.63064,141.97211 621.90672,142.24427 622.18282,142.5769 C 622.45891,142.90951 622.73502,143.30288 623.01111,143.73845 C 623.2872,144.17398 623.56329,144.65181 623.83937,145.14724 C 624.11547,145.64269 624.39158,146.15562 624.66767,146.65759 C 624.94376,147.15956 625.21986,147.65025 625.49594,148.10044 C 625.77203,148.55063 626.04813,148.95986 626.32423,149.30184 C 626.60033,149.64382 626.87641,149.91794 627.1525,150.1046 C 627.4286,150.29127 627.70469,150.38976 627.98079,150.39056 C 628.25688,150.39135 628.53298,150.29373 628.80907,150.10053 C 629.08515,149.90732 629.36126,149.61797 629.63735,149.24849 C 629.91344,148.87902 630.18954,148.42912 630.46564,147.92704 C 630.74171,147.42498 631.01782,146.87073 631.29391,146.30183 C 631.57,145.73296 631.8461,145.14972 632.12219,144.5942 C 632.39829,144.03868 632.67437,143.51144 632.95047,143.05288 C 633.22657,142.59432 633.50265,142.20528 633.77875,141.91791 C 634.05485,141.63052 634.33093,141.44576 634.60703,141.38145 C 634.88313,141.31716 635.15922,141.37425 635.43531,141.55201 C 635.7114,141.72977 635.98749,142.0289 636.26359,142.4285 C 636.53967,142.82809 636.81577,143.3285 637.09187,143.89051 C 637.36795,144.45253 637.64404,145.07601 637.92014,145.70885 C 638.19624,146.34168 638.47233,146.98325 638.74842,147.57695 C 639.02452,148.17063 639.3006,148.71545 639.5767,149.16049 C 639.8528,149.60552 640.12889,149.94957 640.40498,150.15773 C 640.68108,150.36588 640.95717,150.43694 641.23326,150.36019 C 641.50936,150.28343 641.78545,150.05792 642.06155,149.70136 C 642.33763,149.34481 642.61373,148.85669 642.88983,148.28186 C 643.16591,147.70701 643.44201,147.04558 643.71811,146.36234 C 643.9942,145.67908 644.27029,144.97483 644.54639,144.32199 C 644.82248,143.66917 645.09858,143.06912 645.37466,142.58681 C 645.65076,142.10452 645.92686,141.74157 646.20295,141.54036 C 646.47904,141.33917 646.75513,141.3012 647.03123,141.43494 C 647.30732,141.56866 647.58341,141.87505 647.85952,142.32402 C 648.13561,142.77298 648.41169,143.36474 648.68779,144.03484 C 648.96388,144.70491 649.23997,145.45262 649.51607,146.19218 C 649.79217,146.93171 650.06825,147.66156 650.34435,148.29408 C 650.62044,148.92663 650.89654,149.45987 651.17262,149.82631 C 651.44872,150.19273 651.72482,150.39037 652.00089,150.39056" />
         
         
    -    
    -    
    +       id="path4098" />
    +    
    +      
    +      
    +    
         
    @@ -1229,97 +1420,552 @@
            sodipodi:ry="2.8284271" />
         
    +       d="M 590,202.36218 L 590,157.36218" />
       
       
    +    
    +      
    +      
    +      
    +        
    +        Chan1
    +      
    +      
    +        
    +        Chan2
    +      
    +      
    +      
    +      
    +      
    +      
    +      
    +      
    +      
    +      
    +      
    +      
    +        
    +        
    +        
    +        
    +          
    +            
    +            
    +              
    +              x
    +            
    +          
    +        
    +        
    +        
    +      
    +      
    +      
    +      
    +      
    +      
    +      
    +        
    +          
    +          
    +        
    +      
    +      
    +      
    +      
    +        
    +        
    +          
    +            
    +            
    +          
    +          W
    +        
    +      
    +      
    +      
    +        
    +        
    +      
    +      
    +        
    +        
    +      
    +      
    +      
    +        
    +        
    +          
    +          
    +          
    +          
    +        
    +      
    +    
    +  
    +  
    +     inkscape:groupmode="layer"
    +     sodipodi:insensitive="true">
         
    +       sodipodi:nodetypes="ccccccccc"
    +       id="rect2426" />
         
    +       height="131" />
         Anatomy of theAnatomy of thehigh-level model
    +         x="93.090294">high-level model
         The model is an association of The model is an association of Media Objects, grouped to form Processing Pipes, which are a linear chain of data processing modules, starting from an source port and providing an exit point. Optionally, there may be a Optionally, there may be a wiring plug, requesting the exit point to be connected to another pipe. When omitted, the wiring will be figured out automatically.  Several pipes may be joined together by a Several pipes may be joined together by a transition, which in the general case simultanously  treats N media streams. Commonly, a transition simply  combines two streams into one output. Individual Media Objects are attached, located or joined together by transition, which in the general case simultanously treats N media streams. Commonly, a transition simply combines two streams into one output. Individual Media Objects are attached, located or joined together by Placements. A Placement is a handle for a single MObject and contains a list of placement specifications, (called Locating Pin)Besides absolute and relative placement, there is also the possibility of a placement to stick directly to another MObject's placement, e.g. to Besides absolute and relative placement, there is also the possibility of a placement to stick directly to another MObject's placement, e.g. to attach an effect to a clip or an automation data set to an effect.    
    +    
    +    Actually a clip is handled as if it was compirsed of local pipe(s). In the example shown here, a two-channel clip has three effects attached, plus a wiring plug. Each of those attachments is used only if applicable to the media stream type the respective pipe will process. Thus, effectively each channel (sub)clip gets its own specifically adapted processing pipe.  
     
    diff --git a/wiki/renderengine.html b/wiki/renderengine.html
    index d0227f636..fc2ae74ee 100644
    --- a/wiki/renderengine.html
    +++ b/wiki/renderengine.html
    @@ -564,13 +564,24 @@ For every Asset we generate a __Ident tuple__ and a long ID (hash) derived from
     
    The Asset Manager provides an Interface to some internal Database holding all Assets in the current Session and System state. It may be a real Database at some point (and for the moment it's a Hashtable). Each [[Asset]] is registered automatically with the Asset Manager; it can be queried either by it's //identification tuple// or by it's unique ID.
    -
    -
    Placing an MObject relatively to another object such that it should be handled as //attached&nbsp;// to the latter results in several implementation problems. The typical use case is that of an effect attached to a clip or processing pipe.
    -* this attachment is not a globally fixed relation, rather, it typically exists only for some limited time span (e.g. the duration of the basic clip the effect is attached to)
    +
    +
    Placing an MObject relatively to another object such that it should be handled as //attached//&nbsp; to the latter results in several design and implementation challenges. Actually, such an attachment creates a cluster of objects. The typical use case is that of an effect attached to a clip or processing pipe.
    +* attachment is not a globally fixed relation between objects, rather, it typically exists only for some limited time span (e.g. the duration of the basic clip the effect is attached to)
     * the order of attachment is important and the attached placement may create a fork in the signal flow, so we need a way for specifying reproducibly how the resulting wiring should be
    -* when building, we access the information in reversed direction: we have the target object and need to query for all attachements
    +* when building, we access the information in reversed direction: we have the target object and need to query for all attachments
     
    -The first step towards an solution is to isolate the problem; obviously we //need the information about attached objects // only for two isolated tasks, namely when building and for creating a GUI representation. So using a query (function call) interface, the rest of the problem is turned into an implementation detail.
    +The first step towards an solution is to isolate the problem; obviously we don't need to store the objects differently, we just need //information about attached objects//&nbsp; for some quite isolated tasks (namely for creating a GUI representation and for combining attached objects into a [[Pipe]] when building). Resorting to a query (function call) interface should turn the rest of the problem into an implementation detail. Thus +* for an __attachment head__ (= {{{Placement<MObject>}}} to which other objects have been attached) get the ordered list of attachments +* for an __attached placement__ (member of the cluster) get the placement of the corresponding attachment head +* retrieve and break the attachment when //deleting.// + +!!Implementation notes +Attachment is managed within the participating placements, mostly by special [[locating pins|LocatingPin]]. Attachment doesn't necessarily nail down an attached object, rather the behaviour depends on the type of the object and the locating pins actually involved, especially on their order and priority. For example, if an {{{Placement<Effect>}}} doesn't contain any locating pin defining a temporal position, the the attachment will result in the placement inheriting the temporal placement of the attachment head (i.e. the clip this effect has been attached to). But, if on the contrary the effect in question //does// have an additional locating pin, for example relative to another object or even to a fixed time position, this one will "win" and determine the start position of the effect &mdash; it may even move the effect out of the time interval covered by the clip, in which case the attachment has no effect on the clip's processing pipe. +The attachment relation is hierarchical and has a clearly defined //active// and //passive// side: The attachment head is above, but plays the role of the passive partner. But note, this does not mean we are limited to a single attachment head. Actually, each placement has a list of locating pins and thus can attach to several other placements. For example, a transition attaches to at least two local pipes (clips). + +!!!!Relation to memory management +Attachment on itself does //not// keep an object alive. Rather, it's a direct reference and has to be taken into account when deleting an object. On the other hand, processing nodes in the render engine never depend on placements &mdsash; they always refer directly to the MObject instance or even the underlying asset. In the case of MObject instances, the pointer from within the engine will //share ownership// with the placement (remember: both are derived from {{{boost::shared_ptr}}}). +
    Automation is treated as a function over time. It is always tied to a specific Parameter (which can thus be variable over the course of the timeline). All details //how// this function is defined are completely abstracted away. The Parameter uses a ParamProvider to get the value for a given Time (point). Typically, this will use linear or bezier interpolation over a set of keyframes internally. Parameters can be configured to have different value ranges and distribution types (on-off, stepped, continuous, bounded)
    @@ -1127,7 +1138,7 @@ To make the intended use of the classes more clear, consider the following two e
     
    A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. In addition to the properties of a (unspecific) Placement, the ExplicitPlacement specifies a absolute time and track position for locating the Subject
     
    -
    +
    We use Factories
     * for centralizing [[memory management|MemoryManagement]]
     * to support polymorphism (of course...)
    @@ -1147,37 +1158,36 @@ To make the intended use of the classes more clear, consider the following two e
     !!chosen design
     My main goal is to have an easy-to-use interface for the implementer of individual classes using this factory mechanism. The intended use should mimic the standard use of operator new, and at the same time there should be a possibility to configure »real« Factory behaviour in.
     
    -For this reason I make Factory a Functor, and for handling the concrete memory allocation, it inherits from an Allocator class. The users of this factory template typically either parametrize it with existing smart-pointer types and some Allocator instantiation, or they may chose to create a specialized Factory derived from this Factory template. After typically hiding this configuration behind a typedef, the user adds a static field to the class intended to use the Factory and initializes this field with the concrete Factory (this may pass internal ~IDs to the constructor of the Factory and from there on to the underlying Allocator).
    +For this reason I make Factory a Functor, so it can be incorporated as a member into another class, while still looking like a function call to the client code. The users of this factory template typically either parametrize it with existing smart-pointer types, or they may chose to create a specialized Factory derived from this Factory template. After typically hiding this configuration behind a typedef, the user adds a static field to the class intended to use the Factory and initializes this field with the concrete Factory (this may pass internal ~IDs to the constructor of the Factory and from there on to the underlying Allocator).
     {{{
     #include "common/factory.hpp"
     
    -  class Blubb
    +  class Product
         {
    -      int uii ;
    +      int wow_;
     
         public:
    -      typedef lumiera::factory::RefcountPtr<Blubb> Factory;
    +      typedef lumiera::factory::RefcountPtr<Product> Factory;
           static Factory create;
           
    -      Blubb() : uii(42) {} ;
    +      Product() : wow_(42) {} ;
         };
       
    -  /** a static Factory instance 
    -   *  for creating refcounting Ptrs to Blubb objects 
    +  /** storage for a static Factory instance 
    +   *  for creating refcounting Ptrs to Product objects 
        */
    -  Blubb::Factory Blubb::create;  // <----note this is a constructor call
    +  Product::Factory Product::create;  // <----note this is a ctor call
     }}}
    -Now, the clients of {{{class Blubb}}} can create ref-counting pointers to Blubb-objects by doing a fully qualified {{{create()}}} functor call:
    +Now, the clients of {{{class Product}}} can create ref-counting pointers to Product-objects by doing a fully qualified {{{create()}}} functor call:
     {{{
    -  std::tr1::shared_ptr<Blubb> huii = Blubb::create ();  // <----will invoke the default Blubb() ctor 
    -  std::tr1::shared_ptr<Blubb> pfuii = huii;
    +  std::tr1::shared_ptr<Product> huii = Product::create ();  // <----will invoke the default Product() ctor 
    +  std::tr1::shared_ptr<Product> pfuii = huii;
     }}}
     Some further details
    -* Functor inherits (protected) from Allocator, besides that there are no additional requirements.
    -* typically, Allocator provides a overloaded {{{operator new(size_t)}}} etc.
    +* the product class (or base class) cares for using an custom allocator via an overloaded {{{operator new(size_t)}}} if applicable.
     * thus, when Functor or any derived class issues a new XX(), our custom Allocator gains control
     * the Functor-behaviour relies on a custom {{{operator()}}} which can be overridden in derived classes to take various parameter lists.
    -* then, such a Factory class derived from Functor can do specific magic and e.g. create some subclass
    +* additionally, such a Factory class derived from Functor can do specific magic and e.g. create some subclass
     * and, as the created smart-pointer is a template parameter, such a custom Functor can create all sorts of Proxies, wrappers and the like
     
    @@ -1736,7 +1746,7 @@ This Design strives to achieve a StrongSeparation between the low-level Structur [img[Asset Classess|uml/fig130437.png]]
    -
    +
    Of course: Cinelerra currently leaks memory and crashes regularilly. For the newly written code, besides retaining the same level of performance, a main goal is to use methods and techniques known to support the writing of quality code. So, besides the MultithreadConsiderations, a solid strategy for managing the ownership of allocated memory blocks is necessary right from start.
     
     !Problems
    @@ -1762,11 +1772,11 @@ For the case here in question this seems to be the ''resource allocation is cons
     !!!rather harmless
     * Frames (buffers), because they belong to a given [[RenderProcess (=StateProxy)|StateProxy]] and are just passed in into the individual [[ProcNode]]s. This can be handled consistently with conventional methods.
     * each StateProxy belongs to one top-level call to the [[Controller-Facade|Controller]]
    -* same for the builder tools
    +* similar for the builder tools, which belong to a build process. Moreover, they are pooled and reused.
     * the EDL and the defined [[Asset]]s belong together to one Session. If the Session is closed, this means a internal shutdown of the whole ProcLayer, i.e. closing of all GUI representations and terminating all render processes. If these calles are implemented as blocking operations, we can assert that as long as any GUI representation or any render process is running, there is a valid Session and EDL.
     
     !using Factories
    -And, last but not least, doing all actual allocations is the job of the backend. Exceptions being long-lived objects, like the Session or the EDL, which are created once and don't bear the danger of causing memory pressure. Besides, the ProcLayer code shouldn't issue "new" and "delete" when it comes in hand, rather it should use some centralized [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
    +And, last but not least, doing large scale allocations is the job of the backend. Exceptions being long-lived objects, like the Session or the EDL, which are created once and don't bear the danger of causing memory pressure. Besides, the ProcLayer code shouldn't issue "new" and "delete" when it comes in hand, rather it should use some centralized [[Factories]] for all allocation and freeing, so we can redirect these calls down to the backend, which may use pooling or special placement allocators or the like. The rationale is, for modern hardware/architectures, care has to be taken with heap allocations, esp. with many small objects and irregular usage patterns.
     
    @@ -3068,7 +3078,7 @@ if (oldText.indexOf("SplashScreen")==-1) * moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
    -
    +
    //how to classify and describe media streams//
     Media data is understood to appear structured as stream(s) over time. While there may be an inherent internal structuring, at a given perspective ''any stream is a unit and homogeneous''. In the context of digital media data processing, streams are always ''quantized'', which means they appear as a temporal sequence of data chunks called ''frames''.
     
    @@ -3088,15 +3098,15 @@ Media types vary largely and exhibit a large number of different properties, whi
     A stream type is denoted by a ~StreamID, which is an identifier, acting as an unique key for accessing information related to the stream type.
     
     !! Classification
    -Within the Proc-Layer, media streams are treated on the large in a similar manner. But, looking closer, note everyting can be coneected together, wile there are also some classes of media streams which can be considered //equivalent// in most respects. Thus, it seems reasonable to separate the distinction between various media streams into several levels (actually three levels).
    -* Each media belongs to a fundamental ''kind'' of media, examples being __Video__, __Image__, __Audio__, __MIDI__,... Media streams of different kind can be considered somewhat "completely separate" &mdash; just the handling of each media kind follows a common //generic pattern// augmented with specialisations. It is //impossible to connect// media streams of different kind. Under some circumstances there may be the possibility of a transformation though. For example, a still image can be incorporated into video, sound may be visualized, MIDI may control a souond synthesizer.
    -* Below the level of distinct kinds of media streams, we have within every kind a open selection of ''prototypes'', wich may each be "a different thing", but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video which lacks the depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating systems (5.1, 7.1,...), we have common stereponic audio and monaural audio. It is considered important to retain some openness and configuability within this destinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable in some production to distinguish between digitized film and video or NTSC and PAL, while in another production everything is just "video" and can be converted mostly autmatically. The most notable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype.
    -* Besides the distinction in prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchical to the prototype classification, while in detail there will be mostly some sort of dependency. For example, both stereophonig and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, but similarily we may have a dedicated stereo audio stream with the two channels muliplexed into one stream. For dealing with media streams of various implementation type, we need library routines, which also yield a type classification system. Most notably, for raw sound and video data we use the GAVL library, which defines a classification system for buffers and streams.
    -* Besides this type classification, we introduce an ''intention tag''. This is a syntetic classification owned by Lumiera and used for internal wiring decisions. Curently (8/08), we recognize the following intention tags: __Source__, __Raw__, __Intermediary__, __Target__. Only media streams tagget as __Raw__ can be processed.
    +Within the Proc-Layer, media streams are treated on the large in a similar manner. But, looking closer, note everything can be connected together, while on the other hand there may be some classes of media streams which can be considered //equivalent// in most respects. Thus, it seems reasonable to separate the distinction between various media streams into several levels
    +* Each media belongs to a fundamental ''kind'' of media, examples being __Video__, __Image__, __Audio__, __MIDI__,... Media streams of different kind can be considered somewhat "completely separate" &mdash; just the handling of each of those media kinds follows a common //generic pattern// augmented with specialisations. Basically, it is //impossible to connect// media streams of different kind. Under some circumstances there may be the possibility of a transformation though. For example, a still image can be incorporated into video, sound may be visualized, MIDI may control a sound synthesizer.
    +* Below the level of distinct kinds of media streams, within every kind we have an open ended collection of ''prototypes'', which, when compared directly may each be quite distinct and different, but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video lacking depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating sound systems (5.1, 7.1,...), we have common stereophonic and monaural audio. It is considered important to retain some openness and configuability within this level of distinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable for some production to distinguish between digitized film and video NTSC and PAL, while in another production everything is just "video" and can be converted mostly automatically. The most noticeable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype.
    +* Besides the distinction by prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchically related to the prototype classification, while in practice commonly there will be some sort of dependency. For example, both stereophonic and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, as well as we may have a dedicated stereo audio stream with two channels multiplexed into a single stream. For dealing with media streams of various implementation type, we need library routines, which also yield a type classification system. Most notably, for raw sound and video data we use the GAVL library, which defines a classification system for buffers and streams.
    +* Besides the type classification detailed thus far, we introduce an ''intention tag''. This is a synthetic classification owned by Lumiera and used for internal wiring decisions. Currently (8/08), we recognize the following intention tags: __Source__, __Raw__, __Intermediary__ and __Target__. Only media streams tagged as __Raw__ can be processed.
     
    -!! Requirements
    +!! Media handling requirements involving stream type classification
     * set up a buffer and be able to create/retrieve frames of media data.
    -* determine if sources and sinks for media data can be connected, and how.
    +* determine if a given media data source and sink can be connected, and how.
     * determine and enumerate the internal structure of a stream.
     * discover processing facilities
     
    From 790deb16b6627d598e7d05be271b462a6ec97291 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 15 Aug 2008 07:05:43 +0200 Subject: [PATCH 26/40] finished the drawing, added an example session containing 2 EDLs with track tree --- doc/devel/draw/high-level-model.svg | 1834 +++++++++++++++++++++------ wiki/renderengine.html | 6 +- 2 files changed, 1457 insertions(+), 383 deletions(-) diff --git a/doc/devel/draw/high-level-model.svg b/doc/devel/draw/high-level-model.svg index c83f45153..f5f0af8e5 100644 --- a/doc/devel/draw/high-level-model.svg +++ b/doc/devel/draw/high-level-model.svg @@ -25,11 +25,11 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="326.94415" - inkscape:cy="612.06042" + inkscape:zoom="1.4142136" + inkscape:cx="298.73663" + inkscape:cy="289.68644" inkscape:document-units="px" - inkscape:current-layer="lay_Basics" + inkscape:current-layer="lay_Label" inkscape:window-width="1352" inkscape:window-height="1016" inkscape:window-x="0" @@ -48,16 +48,69 @@ gridopacity="0.09019608" showguides="true" inkscape:guide-bbox="false" - inkscape:object-nodes="false" + inkscape:object-nodes="true" gridoriginx="0px" gridtolerance="10000" - inkscape:object-paths="true" + inkscape:object-paths="false" objecttolerance="1" inkscape:object-points="false" inkscape:guide-points="true" guidetolerance="5" /> + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -996,35 +988,6 @@ - - - - - - - @@ -1315,114 +1278,146 @@ + id="g_attachAuto_Legend"> - + transform="translate(480,30)" + id="g_proc_obj_2"> - - + id="g4508" + transform="translate(-440,95)"> + + + - + + + + + + + + Auto - - - - - + sodipodi:linespacing="100%" + id="text_Auto" + style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.60000002;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.71062275;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bitstream Vera Sans">Auto + + + - + id="path4098" + sodipodi:nodetypes="cc" + d="M 540,197.36218 L 540,222.36218" + style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#788888;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Meta + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + global Pipes + + + + EDL-1 + + + + EDL-2 + + + + inkscape:groupmode="layer"> Several pipes may be joined together by a transition, which in the general case simultanously treats N media streams. Commonly, a transition simply combines two streams into one output. transition, which in the general case simultaneously treats N media streams. Commonly, a transition simply combines two streams into one output. Besides absolute and relative placement, there is also the possibility of a placement to stick directly to another MObject's placement, e.g. to attach an effect to a clip or to connect an automation data set to an effect. + id="flowSpan4850">media stream type the respective pipe will process. Thus, effectively each channel (sub)clip gets its own specifically adapted processing pipe. + The Session contains several independent EDLs plus an output bus section (global Pipes). Each EDL holds a collection of MObjects placed within a Tree of Tracks. The wiring, if not defined locally within an MObject's placement, is derived by searching up this track tree and utilizing the wiring plug locating pins found there, if applicable. Besides routing to a global pipe, wiring plugs can also connect to the source port of an Meta-Clip. diff --git a/wiki/renderengine.html b/wiki/renderengine.html index fc2ae74ee..34cf791f3 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -564,7 +564,7 @@ For every Asset we generate a __Ident tuple__ and a long ID (hash) derived from
    The Asset Manager provides an Interface to some internal Database holding all Assets in the current Session and System state. It may be a real Database at some point (and for the moment it's a Hashtable). Each [[Asset]] is registered automatically with the Asset Manager; it can be queried either by it's //identification tuple// or by it's unique ID.
    -
    +
    Placing an MObject relatively to another object such that it should be handled as //attached//&nbsp; to the latter results in several design and implementation challenges. Actually, such an attachment creates a cluster of objects. The typical use case is that of an effect attached to a clip or processing pipe.
     * attachment is not a globally fixed relation between objects, rather, it typically exists only for some limited time span (e.g. the duration of the basic clip the effect is attached to)
     * the order of attachment is important and the attached placement may create a fork in the signal flow, so we need a way for specifying reproducibly how the resulting wiring should be
    @@ -576,8 +576,8 @@ The first step towards an solution is to isolate the problem; obviously we don't
     * retrieve and break the attachment when //deleting.//
     
     !!Implementation notes
    -Attachment is managed within the participating placements, mostly by special [[locating pins|LocatingPin]]. Attachment doesn't necessarily nail down an attached object, rather the behaviour depends on the type of the object and the locating pins actually involved, especially on their order and priority. For example, if an {{{Placement<Effect>}}} doesn't contain any locating pin defining a temporal position, the the attachment will result in the placement inheriting the temporal placement of the attachment head (i.e. the clip this effect has been attached to). But, if on the contrary the effect in question //does// have an additional locating pin, for example relative to another object or even to a fixed time position, this one will "win" and determine the start position of the effect &mdash; it may even move the effect out of the time interval covered by the clip, in which case the attachment has no effect on the clip's processing pipe.
    -The attachment relation is hierarchical and has a clearly defined //active// and //passive// side: The attachment head is above, but plays the role of the passive partner. But note, this does not mean we are limited to a single attachment head. Actually, each placement has a list of locating pins and thus can attach to several other placements. For example, a transition attaches to at least two local pipes (clips).
    +Attachment is managed within the participating placements, mostly by special [[locating pins|LocatingPin]]. Attachment doesn't necessarily nail down an attached object to a specific position, rather the behaviour depends on the type of the object and the locating pins actually involved, especially on their order and priority. For example, if an {{{Placement<Effect>}}} doesn't contain any locating pin defining a temporal position, then the attachment will result in the placement inheriting the temporal placement of the //attachment head// (i.e. the clip this effect has been attached to). But, if on the contrary the effect in question //does// have an additional locating pin, for example relative to another object or even to a fixed time position, this one will "win" and determine the start position of the effect &mdash; it may even move the effect out of the time interval covered by the clip, in which case the attachment has no effect on the clip's processing pipe.
    +The attachment relation is hierarchical and has a clearly defined //active// and //passive// side: The attachment head is the parent node in a tree, but plays the role of the passive partner, to which the child nodes attach. But note, this does not mean we are limited to a single attachment head. Actually, each placement has a list of locating pins and thus can attach to several other placements. For example, a transition attaches to at least two local pipes (clips).
     
     !!!!Relation to memory management
     Attachment on itself does //not// keep an object alive. Rather, it's a direct reference and has to be taken into account when deleting an object. On the other hand, processing nodes in the render engine never depend on placements &mdsash; they always refer directly to the MObject instance or even the underlying asset. In the case of MObject instances, the pointer from within the engine will //share ownership// with the placement (remember: both are derived from {{{boost::shared_ptr}}}).
    
    From 734715d56a59d1222623209b4acaa7498b569b18 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Sat, 16 Aug 2008 05:37:42 +0200
    Subject: [PATCH 27/40] written a text documentating the high-level model
     structure. included png versions of the drawing for the wiki. Intended as a
     lumiera design process entry.
    
    ---
     wiki/draw/high-level1.png | Bin 0 -> 51131 bytes
     wiki/draw/high-level2.png | Bin 0 -> 50486 bytes
     wiki/draw/high-level3.png | Bin 0 -> 92625 bytes
     wiki/renderengine.html    | 105 +++++++++++++++++++++++++++++++++-----
     4 files changed, 91 insertions(+), 14 deletions(-)
     create mode 100644 wiki/draw/high-level1.png
     create mode 100644 wiki/draw/high-level2.png
     create mode 100644 wiki/draw/high-level3.png
    
    diff --git a/wiki/draw/high-level1.png b/wiki/draw/high-level1.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..439cd9e1a82073f4285cf719db3fba1d3fe7725c
    GIT binary patch
    literal 51131
    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

    Ci$r++JmI8tU8N24#2lCm#J0y;CC~5rUlt<9 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 DTz}$4 literal 0 HcmV?d00001 diff --git a/wiki/draw/high-level2.png b/wiki/draw/high-level2.png new file mode 100644 index 0000000000000000000000000000000000000000..33193c53350840d6585b3d3e7cfb6d5df0bff165 GIT binary patch literal 50486 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

    dVS>Lv0WO31aB1Jp-0q-?r|0A2|B++WPe0p00ta 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 literal 0 HcmV?d00001 diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 34cf791f3..9caeef51c 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -1037,7 +1037,7 @@ An [[EDL]] is just a collection of configured and placed objects (and has no add ''State'' is rigorously ''externalized'' and operations are to be ''scheduled'', to simplify locking and error handling. State is either treated similar to media stream data (as addressable and cacheable data frame), or is represented as "parameter" to be served by some [[parameter provider|ParamProvider]]. Automation is just another kind of parameter, i.e. a function, and how this function is calculated is an encapsulated implementation detail (we don't have "bezier automation", and then maybe a "linear automation", a "mask automation" and yet another way to handle transitions)

    -
    +
    This __proc-Layer__ and ~Render-Engine implementation started out as a design-draft by [[Ichthyo|mailto:Ichthyostega@web.de]] in summer 2007. The key idea of this design-draft is to use the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]] for the Render Engine, thus separating completely the //building// of the Render Pipeline from //running,// i.e. doing the actual Render. The Nodes in this Pipeline should process Video/Audio and do nothing else. No more decisions, tests and conditional operations when running the Pipeline. Move all of this out into the configuration of the pipeline, which is done by the Builder.
     
     !Why doesn't the current Cinelerra-2 Design succeed?
    @@ -1056,7 +1056,7 @@ As always, the main goal is //to cut down complexity// by the usual approach to
     
     To achieve this, here we try to separate ''Configuration'' from ''Processing''. Further, in Configuration we try to separate the ''high level view'' (users view when editing) from the ''low level view'' (the actual configuration effective for the calculations). Finally, we try to factor out and encapsulate ''State'' in order to make State explicit.
     
    -The main tool used to implement this separation is the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]]. Here especially we move all decisions and parametrization into the BuildProcess. The Nodes in the render pipeline should process Video/Audio and do nothing else. No more decisions, tests and conditional operations when running the Pipeline. Move all of this out into the configuration of the pipeline, which is done by the Builder. Make the actual processing nodes Template classes, parametrized by the color model and number of components. Make all Nodes of equal footing with each other, able to be connected freely within the limitations of the necessary input and output. Make the OpenGL rendering into alternate implementation of some operations together with an alternate signal flow (usable only if the whole Pipeline can be built up to support this changed signal flow), thus factoring out all the complexities of managing the data flow between core and hardware accelerated rendering out of the implementation of the actual processing. Introduce separate control data connections for the automation data, separating the case of true multi-channel-effects from the case where one node just gets remote controlled by another node (or two nodes using the same automation data).
    +The main tool used to implement this separation is the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]]. Here especially we move all decisions and parametrization into the BuildProcess. The Nodes in the render pipeline should process Video/Audio and do nothing else. All decisions, tests and conditional operations are factored out of the render process and handled as configuration of the pipeline, which is done by the Builder. The actual color model and number of ports is configured in by a pre-built wiring descriptor. All Nodes are of equal footing with each other, able to be connected freely within the limitations of the necessary input and output. OpenGL and renderfarm support can be configured in as an alternate implementation of some operations together with an alternate signal flow (usable only if the whole Pipeline can be built up to support this changed signal flow), thus factoring out all the complexities of managing the data flow between core and hardware accelerated rendering. We introduce separate control data connections for the automation data, separating the case of true multi-channel-effects from the case where one node just gets remote controlled by another node (or two nodes are using the same automation data).
     
     Another pertinent theme is to make the basic building blocks simpler, while on the other hand gaining much more flexibility for combining these building blocks. For example we try to unfold any "internal-multi" effects into separate instances (e.g. the possibility of having an arbitrary number of single masks at any point of the pipeline instead of having one special masking facility encompassing multiple sub-masks. Similarly, we treat the Objects in the EDL in a more uniform manner and gain the possibility to [[place|Placement]] them in various ways.
     
    @@ -1289,7 +1289,44 @@ For this Lumiera design, we could consider making GOP just another raw media dat &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
    -
    +
    +
    While the low-level model holds the data used for carrying out the actual media data processing (=rendering), the high-level model is what the user works upon when performing edit operations through the GUI (or script driven in &raquo;headless mode&laquo;). Its building blocks and combination rules determine largely what structures can be created within the [[Session]].
    +On the whole, it is a collection of [[media objects|MObjects]] stuck together and arranged by [[placements|Placement]].
    +
    +Basically, the structure of the high-level model is is a very open and flexible one &mdash; every valid connection of the underlying object types is allowed &mdash; but the transformation into a low-level node network for rendering follows certain patterns and only takes into account any objects reachable while processing the session data in accordance to these patterns. Taking into account the parameters and the structure of these objects visited when building, the low-level render node network is configured in detail.
    +
    +The fundamental metaphor or structural pattern is to create processing ''pipes'', which are a linear chain of data processing modules, starting from an source port and providing an exit point. [[Pipes|Pipe]] are a //concept or pattern,// they don't exist as objects. Each pipe has an input side and an output side and is in itself something like a Bus treating a single [[media stream|StreamType]] (but this stream may still have an internal structure, e.g. several channels related to a spatial audio system). Other processing entities like effects and transitions can be placed (attached) at the pipe, resulting them to be appended to form this chain. Optionally, there may be a ''wiring plug'', requesting the exit point to be connected to another pipe. When omitted, the wiring will be figured out automatically.
    +Thus, when making an connection //to// a pipe, output data will be sent to the //source port// (input side) of the pipe, wheras when making a connection //from// a pipe, data from it's exit point will be routed to the destination. Incidentally, the low-level model and the render engine employ //pull-based processing,// but this is rather of no relevance for the high-level model.
    +
    +
    +
    +[img[draw/high-level1.png]]
    +Normally, pipes are limited to a //strictly linear chain// of data processors ("''effects''") working on a single data stream type, and consequently there is a single ''exit point'' which may be wired to an destination. As an exception to this rule, you may insert wire tap nodes (probe points), which explicitly may send data to an arbitrary input port; they are never wired automatically. It is possible to create cyclic connections by such arbitrary wiring, which will be detected by the builder and flagged as an error.
    +
    +While pipes have a rather rigid and limited structure, it is allowed to make several connections to and from any pipe &mdash; even connections requiring an stream type conversion. It is not even necessary to specify //any// output destination, because then the wiring will be figured out automatically by searching the context and finally using some general rule. Connecting multiple outputs to the input of another pipe automatically creates a ''mixing step'' (which optionally can be controlled by a fader). Several pipes may be joined together by a ''transition'', which in the general case simultaneously treats N media streams. Of course, the most common case is to combine two streams into one output, thereby also mixing them. Most available transition plugins belong to this category, but, as said, the model isn't limited to this simple case, and moreover it is possible to attach several overlapping transitions covering the same time interval.
    +
    +Individual Media Objects are attached, located or joined together by ''Placements''. A [[Placement]] is a handle for a single MObject (implemented as a refcounting smart-ptr) and contains a list of placement specifications, called LocatingPin. Adding an placement to the session acts as if creating an //instance.// (it behaves like a clone in case of multiple placements of the same object). Besides absolute and relative placement, there is also the possibility of a placement to stick directly to another MObject's placement, e.g. for attaching an effect to a clip or to connect an automation data set to an effect. This //stick-to placement// creates sort of a loose clustering of objects: it will derive the position from the placement it is attached to. Note that while the length and the in/out points are a //property of the ~MObject,// it's actual location depends on how it is //placed// and thus can be maintained quite dynamically. Note further that effects can have an length on their own, thus by using these attachement mechaics, the wiring and configuration within the high-level model can be quite time dependant.
    +
    +[>img[draw/high-level2.png]]
    +
    +
    +
    +Actually a ''clip'' is handled as if it was comprised of local pipe(s). In the example shown here, a two-channel clip has three effects attached, plus a wiring plug. Each of those attachments is used only if applicable to the media stream type the respective pipe will process. As the clip has two channels (e.g. video and audio), it will have two ''source ports'' pulling from the underlying media. Thus, as showed in the drawing to the right, by chaining up any attached effect applicable to the respective stream type defined by the source port, effectively each channel (sub)clip gets its own specifically adapted processing pipe.
    +
    +@@clear(right):display(block):@@
    +!!Example of an complete Session
    +[img[draw/high-level3.png]]
    +The Session contains several independent [[EDL]]s plus an output bus section (''global Pipes''). Each EDL holds a collection of ~MObjects placed within a ''tree of tracks''. 
    +Within Lumiera, tracks are a rather passive means for organizing media objects, but aren't involved into the data processing themselves. The possibility of nesting tracks allows for easy grouping. Like the other objects, tracks are connected together by placements: A track holds the list of placements of its child tracks. Each EDL holds a single placement pointing to the root track. 
    +
    +As placements have the ability to cooperate and derive any missing placement specifications, this creates a hierarchical structure throughout the session, where parts on any level behave similar if applicable. For example, when a track is anchored to some external entity (label, sync point in sound, etc), all objects placed relatively to this track will adjust and follow automatically. This relation between the track tree and the individual objects is especially important for the wiring, which, if not defined locally within an ~MObject's placement, is derived by searching up this track tree and utilizing the wiring plug locating pins found there, if applicable. In the default configuration, the placement of an EDL's root track contains a wiring plug for video and another wiring plug for audio. This setup is sufficient for getting every object within this EDL wired up automatically to the correct global output pipe. Moreover, when adding another wiring plug to some sub track, we can intercept and reroute the connections of all objects creating output of this specific stream type within this track and on all child tracks.
    +
    +Besides routing to a global pipe, wiring plugs can also connect to the source port of an ''meta-clip''. In this example session, the outputs of ~EDL-2 as defined by locating pins in it's root track's placement, are directed to the source ports of a [[meta-cllip|VirtualClip]] placed within ~EDL-1. Thus, within ~EDL-1, the contents of ~EDL-2 appear like a pseudo-media, from which the (meta) clip has been taken. They can be adorned with effects and processed further completely similar to a real clip.
    +
    +Finally, this example shows an ''automation'' data set controlling some parameter of an effect contained in one of the global pipes. From the effect's POV, the automation is simply a ParamProvider, i.e a function yielding a scalar value over time. The automation data set may be implemented as a bézier curve, or by a mathematical function (e.g. sine or fractal pseudo random) or by some captured and interpolated data values. Interestingly, in this example the automation data set has been placed relatively to the meta clip (albeit on another track), thus it will follow and adjust when the latter is moved.
    +
    +
    +
    This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Lumiera Renderengine, the Builder and the related parts.
     
     * [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
    @@ -1307,7 +1344,7 @@ For this Lumiera design, we could consider making GOP just another raw media dat
     * [[identifying the basic Builder operations|BasicBuildingOperations]] and [[planning the Implementation|PlanningNodeCreatorTool]]
     * [[how to handle »attached placement«|AttachedPlacementProblem]]
     * working out the [[basic building situations|BuilderPrimitives]] and [[mechanics of rendering|RenderMechanics]]
    -* how to classify and [[describe media stream types|StreamTypeDescr]]
    +* how to classify and [[describe media stream types|StreamType]]
     
    @@ -2981,8 +3018,9 @@ The Session object is a singleton &mdash; actually it is a »~PImpl«-Facade * a collection of ''global Pipes'' * the ''Fixture'' with a possibility to [[(re)build it|PlanningBuildFixture]]
    -
    -
    The [[Session]] (sometimes also called //Project//) contains all informations and objects to be edited by the User. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[EDL (Edit Decision List)|EDL]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application.
    +
    +
    The [[Session]] (sometimes also called //Project// ) contains all informations and objects to be edited by the User. It can be saved and loaded. The individual Objects within the Session, i.e. Clips, Media, Effects, are contained in one (or several) collections within the Session, which we call [[EDL (Edit Decision List)|EDL]]. Moreover, the sesion contains references to all the Media files used, and it contains various default or user defined configuration, all being represented as [[Asset]]. At any given time, there is //only one current session// opened within the application.
    +The Session is close to what is visible in the GUI. From a user's perspective, you'll find a timeline-like structure, where various Media Objects are arranged and placed. The available building blocks and the rules how they can be combined together form Lumiera's [[high-level data model|HighLevelModel]]. Basically, besides the [[media objects|MObjects]] there are data connections and all processing is organized around processing chains or [[pipes|Pipe]], which can be either global (in the Session) or local (in real or virtual clips)
     
     !!!larger projects
     For larger editing projects the simple structure of a session containing "the" timeline is not sufficient. Rather, we have several timelines, e.g. one for each scene. Or we could have several layered or nested timelines (compositional work, multimedia productions). To support these cases without making the default case more complicated, Lumiera introduces a //focus// for selecting the //current EDL,// which will receive all editing operations.
    @@ -2999,6 +3037,8 @@ The possibility of having multiple ~EDLs helps organizing larger projects. Each
     !!!default configuration
     While all these possibilities may seem daunting, there is a simple default configuration loaded into any pristine new session:
     It will contain a global video and audio out pipe, just one EDL with a single track; this track will have a internal video and audio pipe (bus) configured with one fading device sending to the global output ports. So, by adding some clip with a simple absolute placement to this track and some time position, the clip gets connected and rendered, after [[(re)building|PlanningBuildFixture]] the [[Fixture]] and passing the result to the [[Builder]] &mdash; and using the resulting render nodes network (Render Engine).
    +
    +&rarr; [[anatomy of the high-level model|HighLevelModel]]
     
    @@ -3078,7 +3118,22 @@ if (oldText.indexOf("SplashScreen")==-1) * moreover, it contains methods to communicate with other state relevant parts of the system, thereby shielding the rendering code from any complexities of Thread communication if necessary. (thus the name Proxy)
    -
    +
    +
    The stream Prototype is part of the specification of a media stream's type. It is a semantic (or problem domain oriented) concept and should be distinguished from the actual implementation type of the media stream. The latter is provided by an [[library implementation|StreamTypeImplFacade]]. While there are some common predefined prototypes, mostly, they are defined within the concrete [[Session]] according to the user's needs. 
    +
    +Prototypes form an open (extensible) collection, though each prototype belongs to a specific media kind ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}). The ''distinguishing property'' of a stream prototype is that any [[Pipe]] can process //streams of a specific prototype only.// Thus, two streams with different prototype can be considered "something quite different" from the users point of view, while two streams belonging to the same prototype can be considered equivalent (and will be converted automatically when their implementation types differ). Note this definition is //deliberately fuzzy,// because it depends on the actual situation of the project in question.
    +
    +Consequently, as we can't get away with an fixed Enum of all stream prototypes, the implementation must rely on a query interface. The intention is to provide a basic set of rules for deciding queries about the most common stream prototypes; besides, a specific session may inject additional rules or utilize a completely different knowledge base. Thus, for a given StreamTypeDescriptor specifying a prototype
    +* we can get a [[default|DefaultsManagement]] implementation type
    +* we can get a default prototype to a given implementation type by a similar query
    +* we can query if a implementation type in question can be //subsumed// under this prototype
    +* we can determine if another prototype is //convertible//
    +
    +!!Examples
    +NTSC and PAL video, video versus digitized film, HD video versus SD video, 3D versus flat video, cinemascope versus 4:3, stereophonic versus monaural, periphonic versus panoramic sound, Ambisonics versus 5.1, dolby versus dts,...
    +
    +
    +
    //how to classify and describe media streams//
     Media data is understood to appear structured as stream(s) over time. While there may be an inherent internal structuring, at a given perspective ''any stream is a unit and homogeneous''. In the context of digital media data processing, streams are always ''quantized'', which means they appear as a temporal sequence of data chunks called ''frames''.
     
    @@ -3091,16 +3146,16 @@ Media data is understood to appear structured as stream(s) over time. While ther
     * the __~Stream-Type__ describes the kind of media data contained in the stream
     
     ! Problem of Stream Type Description
    -Media types vary largely and exhibit a large number of different properties, which can't be subsumed under a single classification scheme. But on the other hand, we want to deal with media objects in a uniform and generic manner, because on the large all kinds of media behave somewhat similar. The problem is, these similarities disappear when describing media with logical precision. Thus we are forced into specialized handling and operations for each kind of media, while we want to implement a generic handling concept.
    +Media types vary largely and exhibit a large number of different properties, which can't be subsumed under a single classification scheme. But on the other hand, we want to deal with media objects in a uniform and generic manner, because generally all kinds of media behave somewhat similar. But the problem is, these similarities disappear when describing media with logical precision. Thus we are forced into specialized handling and operations for each kind of media, while we want to implement a generic handling concept.
     
     ! Stream Type handling in the Proc-Layer
     !! Identification
    -A stream type is denoted by a ~StreamID, which is an identifier, acting as an unique key for accessing information related to the stream type.
    +A stream type is denoted by a StreamTypeID, which is an identifier, acting as an unique key for accessing information related to the stream type. It corresponds to an StreamTypeDescriptor record, containing an &mdash; //not necessarily complete// &mdash; specification of the stream type, according to the classification detailed below.
     
     !! Classification
    -Within the Proc-Layer, media streams are treated on the large in a similar manner. But, looking closer, note everything can be connected together, while on the other hand there may be some classes of media streams which can be considered //equivalent// in most respects. Thus, it seems reasonable to separate the distinction between various media streams into several levels
    -* Each media belongs to a fundamental ''kind'' of media, examples being __Video__, __Image__, __Audio__, __MIDI__,... Media streams of different kind can be considered somewhat "completely separate" &mdash; just the handling of each of those media kinds follows a common //generic pattern// augmented with specialisations. Basically, it is //impossible to connect// media streams of different kind. Under some circumstances there may be the possibility of a transformation though. For example, a still image can be incorporated into video, sound may be visualized, MIDI may control a sound synthesizer.
    -* Below the level of distinct kinds of media streams, within every kind we have an open ended collection of ''prototypes'', which, when compared directly may each be quite distinct and different, but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video lacking depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating sound systems (5.1, 7.1,...), we have common stereophonic and monaural audio. It is considered important to retain some openness and configuability within this level of distinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable for some production to distinguish between digitized film and video NTSC and PAL, while in another production everything is just "video" and can be converted mostly automatically. The most noticeable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype.
    +Within the Proc-Layer, media streams are treated largely in a similar manner. But, looking closer, note everything can be connected together, while on the other hand there may be some classes of media streams which can be considered //equivalent// in most respects. Thus, it seems reasonable to separate the distinction between various media streams into several levels
    +* Each media belongs to a fundamental ''kind'' of media, examples being __Video__, __Image__, __Audio__, __MIDI__,... Media streams of different kind can be considered somewhat "completely separate" &mdash; just the handling of each of those media kinds follows a common //generic pattern// augmented with specialisations. Basically, it is //impossible to connect// media streams of different kind. Under some circumstances there may be the possibility of a //transformation// though. For example, a still image can be incorporated into video, sound may be visualized, MIDI may control a sound synthesizer.
    +* Below the level of distinct kinds of media streams, within every kind we have an open ended collection of ''prototypes'', which, when compared directly may each be quite distinct and different, but which may be //rendered//&nbsp; into each other. For example, we have stereoscopic (3D) video and we have the common flat video lacking depth information, we have several spatial audio systems (Ambisonics, Wave Field Synthesis), we have panorama simulating sound systems (5.1, 7.1,...), we have common stereophonic and monaural audio. It is considered important to retain some openness and configurability within this level of distinction, which means this classification should better be done by rules then by setting up a fixed property table. For example, it may be desirable for some production to distinguish between digitized film and video NTSC and PAL, while in another production everything is just "video" and can be converted mostly automatically. The most noticeable consequence of such a distinction is that any Bus or [[Pipe]] is always limited to a media stream of a single prototype. (&rarr; [[more|StreamPrototype]])
     * Besides the distinction by prototypes, there are the various media ''implementation types''. This classification is not necessarily hierarchically related to the prototype classification, while in practice commonly there will be some sort of dependency. For example, both stereophonic and monaural audio may be implemented as 96kHz 24bit PCM with just a different number of channel streams, as well as we may have a dedicated stereo audio stream with two channels multiplexed into a single stream. For dealing with media streams of various implementation type, we need library routines, which also yield a type classification system. Most notably, for raw sound and video data we use the GAVL library, which defines a classification system for buffers and streams.
     * Besides the type classification detailed thus far, we introduce an ''intention tag''. This is a synthetic classification owned by Lumiera and used for internal wiring decisions. Currently (8/08), we recognize the following intention tags: __Source__, __Raw__, __Intermediary__ and __Target__. Only media streams tagged as __Raw__ can be processed.
     
    @@ -3111,6 +3166,28 @@ Within the Proc-Layer, media streams are treated on the large in a similar manne
     * discover processing facilities
     
    +
    +
    A description and classification record usable to find out about the properties of a media stream. The stream type descriptor can be accessed using an unique StreamTypeID. The information contained in this descriptor record can intentionally be //incomplete,// in which case the descriptor captures a class of matching media stream types. The following information is maintained:
    +* fundamental ''kind'' of media: {{{VIDEO, IMAGE, AUDIO, MIDI,...}}}
    +* stream ''prototype'': this is the abstract high level media type, like NTSC, PAL, Film, 3D, Ambisonics, 5.1, monaural,...
    +* stream ''implementation type'' accessible by virtue of an StreamTypeImplFacade
    +* the ''intended usage category'' of this stream: {{{SOURCE, RAW, INTERMEDIARY, TARGET}}}.
    +&rarr; see &raquo;[[Stream Type|StreamType]]&laquo; detailed specification
    +&rarr; more [[about prototypes|StreamPrototype]]
    +
    +
    +
    This ID is an symbolic key linked to a StreamTypeDescriptor. The predicate {{{stream(ID)}}} specifies a media stream with the StreamType as detailed by the corresponding descriptor (which may contain complete or partial data defining the type).
    +
    +
    +
    Common interface for dealing with the implementation of media stream data. From a high level perspective, the various kinds of media ({{{VIDEO, IMAGE, AUDIO, MIDI,...}}}) exhibit similar behaviour, while on the implementation level not even the common classification can be settled down to a complete general and useful scheme. Thus, we need separate library implementations for deailing with the various sorts of media data, all providing at least a set of basic operations:
    +* set up a buffer
    +* create or accept a frame
    +* get an tag describing the precise implementation type
    +* ...?
    +
    +&rarr; see also &raquo;[[Stream Type|StreamType]]&laquo;
    +
    +
    This design lays great emphasis on separating all those components and subsystems, which are considered not to have a //natural link// of their underlying concepts. This often means putting some additional constraints on the implementation, so basically we need to rely on the actual implementation to live up to this goal. In many cases it may seem to be more natural to "just access the necessary information". But on the long run this coupling of not-directly related components makes the whole codebase monolithic and introduces lots of //accidental complexity.//
     
    @@ -4390,10 +4467,10 @@ Using transitions is a very basic task and thus needs viable support by the GUI.
     Because of this experience, ichthyo wants to support a more general case of transitions, which have N output connections, behave similar to their "simple" counterpart, but leave out the mixing step. As a plus, such transitions can be inserted at the source ports of N clips or between any intermediary or final output pipes as well. Any transition processor capable of handling this situation should provide some flag, in order to decide if he can be placed in such a manner. (wichin the builder, encountering a  inconsistently placed transition is just an [[building error|BuildingError]])
     
    -
    +
    A ''~Meta-Clip'' or ''Virtual Clip'' (both are synonymous) denotes a clip which doesn't just pull media streams out of a source media asset, but rather provides the results of rendering a complete sub-network. In all other respects it behaves exactly like a "real" clip, i.e. it has [[source ports|ClipSourcePort]], can have attached effects (thus forming a local render pipe) and can be placed and combined with other clips. Depending on what is wired to the source ports, we get two flavours:
     * a __placeholder clip__ has no "embedded" content. Rather, by virtue of placements and wiring requests, the output of some other pipe somewhere in the session will be wired to the clip's source ports. Thus, pulling data from this clip will effectively pull from these source pipes wired to it.
    -* a __nested EDL__ is like the other ~EDLs in the Session, just that any missing placement properties will be derived from the Virtual Clip, which is thought as to "contain" the objects of the nested EDL. Typically, you'd also [[configure the tracks|TrackHandling]] of the "inner" EDL such as to connect any output to the source ports of the Virtual Clip.
    +* a __nested EDL__ is like the other ~EDLs in the Session, just that any missing placement properties will be derived from the Virtual Clip, which is thought as to "contain" the objects of the nested EDL. Typically, this also [[configures the tracks|TrackHandling]] of the "inner" EDL such as to connect any output to the source ports of the Virtual Clip.
     
     Like any "real" clip, Virtual Clips have a start offset and a length, which will simply translate into an offset of the frame number pulled from the Virtual Clip's source connection or embedded EDL, making it possible to cut, splice, trim and roll them as usual. This of course implies we can have several instances of the same virtual clip with different start offset and length placed differently. The only limitation is that we can't handle cyclic dependencies for pulling data (which has to be detected and flagged as an error by the builder)
     
    From 19156f3a348bc82c1ea38c00ce9151fa4abed79c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 19 Aug 2008 03:58:22 +0200 Subject: [PATCH 28/40] buildfix --- src/gui/panels/timeline-panel.cpp | 3 ++- src/proc/Makefile.am | 13 ------------- wiki/compatibility.html | 6 ++++-- wiki/index.html | 5 +++-- 4 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 00a3c3f0f..709a18ac5 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -50,7 +50,8 @@ TimelinePanel::TimelinePanel() : toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in)); toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out)); - toolbar.set_icon_size(IconSize(ICON_SIZE_LARGE_TOOLBAR)); +// doesn't compile on Etch +// toolbar.set_icon_size(IconSize(ICON_SIZE_LARGE_TOOLBAR)); toolbar.set_toolbar_style(TOOLBAR_ICONS); // Add the toolbar diff --git a/src/proc/Makefile.am b/src/proc/Makefile.am index 9c6ca64e0..10de0d119 100644 --- a/src/proc/Makefile.am +++ b/src/proc/Makefile.am @@ -176,15 +176,6 @@ noinst_HEADERS += \ $(liblumiproc_a_srcdir)/asset/track.hpp \ $(liblumiproc_a_srcdir)/asset/unknown.hpp \ $(liblumiproc_a_srcdir)/controllerfacade.hpp \ - $(liblumiproc_a_srcdir)/engine/aframe.hpp \ - $(liblumiproc_a_srcdir)/engine/arender.hpp \ - $(liblumiproc_a_srcdir)/engine/codecadapter.hpp \ - $(liblumiproc_a_srcdir)/engine/exitnode.hpp \ - $(liblumiproc_a_srcdir)/engine/glbuf.hpp \ - $(liblumiproc_a_srcdir)/engine/glpipe.hpp \ - $(liblumiproc_a_srcdir)/engine/glrender.hpp \ - $(liblumiproc_a_srcdir)/engine/hub.hpp \ - $(liblumiproc_a_srcdir)/engine/link.hpp \ $(liblumiproc_a_srcdir)/engine/mask.hpp \ $(liblumiproc_a_srcdir)/engine/pluginadapter.hpp \ $(liblumiproc_a_srcdir)/engine/procnode.hpp \ @@ -192,14 +183,10 @@ noinst_HEADERS += \ $(liblumiproc_a_srcdir)/engine/renderengine.hpp \ $(liblumiproc_a_srcdir)/engine/source.hpp \ $(liblumiproc_a_srcdir)/engine/trafo.hpp \ - $(liblumiproc_a_srcdir)/engine/vframe.hpp \ - $(liblumiproc_a_srcdir)/engine/vrender.hpp \ $(liblumiproc_a_srcdir)/engine/processor.hpp \ - $(liblumiproc_a_srcdir)/frame.hpp \ $(liblumiproc_a_srcdir)/mobject/builder/assembler.hpp \ $(liblumiproc_a_srcdir)/mobject/builder/buildertool.hpp \ $(liblumiproc_a_srcdir)/mobject/builder/conmanager.hpp \ - $(liblumiproc_a_srcdir)/mobject/builder/nodecreatertool.hpp \ $(liblumiproc_a_srcdir)/mobject/builder/segmentationtool.hpp \ $(liblumiproc_a_srcdir)/mobject/builder/toolfactory.hpp \ $(liblumiproc_a_srcdir)/mobject/builderfacade.hpp \ diff --git a/wiki/compatibility.html b/wiki/compatibility.html index 0d6077a15..5327731c0 100644 --- a/wiki/compatibility.html +++ b/wiki/compatibility.html @@ -747,7 +747,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS } //}}}
    -
    +
    ! Programming Languages
     * C
     ** a C99 compatible compiler, some GCC extensions are used, most are optional.
    @@ -782,13 +782,15 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
     * boost (see above, version 1.35 works too)
     * NoBug
     * [[GAVL|http://gmerlin.sourceforge.net/gavl.html]] (1.0.0) 
    -* for the GUI: gtkmm-2.4 gdl-1.0 cairomm-1.0 xv
    +* for the GUI: gtkmm-2.4 gdl-1.0 libglibmm-2.4 cairomm-1.0 xv
     ** libgtkmm-2.4-dev (>=2.8)
     ** libcairomm-1.0-dev (>=0.6.0)
     ** libgdl-1-dev (>=0.6.1)
     *** libbonoboui2-dev (>=2.14.0)
    +** libgtkmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
     ** libxv-dev ~~(1.0.2 is known to work)~~
     
    +
     //usually, newer versions are OK//
     
     
    diff --git a/wiki/index.html b/wiki/index.html index d3d5716f6..55a65446f 100644 --- a/wiki/index.html +++ b/wiki/index.html @@ -759,7 +759,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS } //}}}
    -
    +
    for __Building__
     * gcc (4.1), glibc6 (2.3), libstdc++6 (4.1)
     * [[build system|BuildSystem]] dependencies: SCons (0.96.90), Python (2.4), pkg-config
    @@ -773,11 +773,12 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
     ** libboost-program-options1.34.1 (>=1.34.1-2) ''NOTE: binary dependency''
     ** libboost-regex-dev (>=1.34.1-2)
     ** libboost-regex1.34.1 (>=1.34.1-2) ''binary..''
    -* for the GUI: gtkmm-2.4 gdl-1.0 cairomm-1.0 xv
    +* for the GUI: gtkmm-2.4 gdl-1.0 libglibmm-2.4 cairomm-1.0 xv
     ** libgtkmm-2.4-dev (>=2.8)
     ** libcairomm-1.0-dev (>=0.6.0)
     ** libgdl-1-dev (>=0.6.1)
     *** libbonoboui2-dev (>=2.14.0)
    +** libgtkmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
     ** libxv-dev (>=1.0.2)
     //usually, newer versions are OK//
     
    
    From 878ce2319b1afca650d990ef05fbe62189b009a2 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Tue, 19 Aug 2008 05:03:29 +0200
    Subject: [PATCH 29/40] integrated SVG Icon rendering into the SCons build
    
    ---
     .gitignore                        |  2 +-
     SConstruct                        | 22 +++++++++++--
     admin/SConscript                  | 17 ++++++++++
     admin/render-icon.py              | 15 +++++++--
     admin/scons/Buildhelper.py        | 55 +++++++++++++++++++++++++++++--
     admin/scons/LumieraEnvironment.py |  8 +++--
     wiki/compatibility.html           |  4 +--
     wiki/index.html                   |  4 +--
     8 files changed, 113 insertions(+), 14 deletions(-)
     create mode 100644 admin/SConscript
    
    diff --git a/.gitignore b/.gitignore
    index 10dbaee23..a42a27923 100644
    --- a/.gitignore
    +++ b/.gitignore
    @@ -5,7 +5,7 @@
     *.os
     *.gch
     ,valgrind.log*
    -admin/scons/*.pyc
    +*.pyc
     optcache
     Makefile.in
     build/*
    diff --git a/SConstruct b/SConstruct
    index 47dc7ef8a..cbe45b1b3 100644
    --- a/SConstruct
    +++ b/SConstruct
    @@ -37,6 +37,7 @@ BINDIR           = 'bin'
     TESTDIR          = 'tests'
     ICONDIR          = 'icons'
     VERSION          = '0.1+pre.01'
    +SVGRENDERER      = 'admin/render-icon'
     #-----------------------------------Configuration
     
     # NOTE: scons -h for help.
    @@ -68,6 +69,7 @@ def setupBasicEnvironment():
                    , CCFLAGS='-Wall '                                       # -fdiagnostics-show-option 
                    )
         
    +    RegisterIcon_Builder(env,SVGRENDERER)
         RegisterPrecompiledHeader_Builder(env)
         handleNoBugSwitches(env)
         
    @@ -225,6 +227,10 @@ def configurePlatform(env):
         if not conf.CheckPkgConfig('gtkmm-2.4', 2.8):
             print 'Unable to configure GTK--, exiting.'
             Exit(1)
    +        
    +    if not conf.CheckPkgConfig('glibmm-2.4', '2.16'):
    +        print 'Unable to configure Lib glib--, exiting.'
    +        Exit(1)
         
         if not conf.CheckPkgConfig('cairomm-1.0', 0.6):
             print 'Unable to configure Cairo--, exiting.'
    @@ -234,6 +240,10 @@ def configurePlatform(env):
             print 'Unable to configure the GNOME DevTool Library, exiting.'
             Exit(1)
         
    +    if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'):
    +        print 'Need rsvg Library for rendering icons.'
    +        Exit(1)
    +        
         if not conf.CheckPkgConfig('xv'): Exit(1)
     #   if not conf.CheckPkgConfig('xext'): Exit(1)
     #   if not conf.CheckPkgConfig('sm'): Exit(1)
    @@ -292,16 +302,24 @@ def defineBuildTargets(env, artifacts):
         artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', objplug)
         
         # the Lumiera GTK GUI
    -    envgtk  = env.Clone().mergeConf(['gtkmm-2.4','cairomm-1.0','gdl-1.0','xv','xext','sm'])
    +    envgtk  = env.Clone().mergeConf(['gtkmm-2.4','cairomm-1.0','gdl-1.0','librsvg-2.0','xv','xext','sm'])
         objgui  = srcSubtree(envgtk,'$SRCDIR/gui')
         
    +    # render and install Icons
    +    vector_icon_dir      = env.subst('$ICONDIR/svg')
    +    prerendered_icon_dir = env.subst('$ICONDIR/prerendered')
    +    artifacts['icons']   = ( [env.IconRender(f) for f in scanSubtree(vector_icon_dir,      ['*.svg'])]
    +                           + [env.IconCopy(f)   for f in scanSubtree(prerendered_icon_dir, ['*.png'])]
    +                           )
    +    
         artifacts['lumigui'] = ( envgtk.Program('$BINDIR/lumigui', objgui + core)
    -                           + env.Install('$BINDIR', env.Glob('$ICONDIR/*.png'))
                                + env.Install('$BINDIR', env.Glob('$SRCDIR/gui/*.rc'))
    +                           + artifacts['icons']
                                )
         
         # call subdir SConscript(s) for independent components
         SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts core')
    +    SConscript(dirs=['admin'], exports='env envgtk artifacts core')
         SConscript(dirs=[TESTDIR], exports='env artifacts core')
     
     
    diff --git a/admin/SConscript b/admin/SConscript
    new file mode 100644
    index 000000000..d0ad7b84e
    --- /dev/null
    +++ b/admin/SConscript
    @@ -0,0 +1,17 @@
    +# -*- python -*-
    +##
    +## SConscript  -  SCons buildscript for helper tools (called by SConstruct)
    +##
    +
    +Import('env','envgtk','artifacts','core')
    +
    +vgsuppr = env.Program('#$BINDIR/vgsuppression',['vgsuppression.c']+core)
    +rsvg    = envgtk.Program('#$BINDIR/rsvg-convert','rsvg-convert.c')
    +
    +artifacts['tools'] += [ vgsuppr    ## for supressing false valgrind alarms
    +                      + rsvg       ## for rendering SVG icons (uses librsvg)
    +                      ]
    +
    +# Rendering the SVG Icons depends on rsvg-convert
    +env.Depends(artifacts['icons'], rsvg)
    +
    diff --git a/admin/render-icon.py b/admin/render-icon.py
    index ac8ab2453..26bdb3653 100755
    --- a/admin/render-icon.py
    +++ b/admin/render-icon.py
    @@ -68,7 +68,7 @@ def parsePlateLayer( layer ):
       return rectangles
     
     def parseSVG( file_path ):
    -  print "Rendering " + file_path
    +  print "Parsing " + file_path
       svgdoc = minidom.parse(file_path)
       for root_node in svgdoc.childNodes:
         if root_node.nodeType == minidom.Node.ELEMENT_NODE:
    @@ -104,6 +104,9 @@ def renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size):
       # Prepare a Cairo context
       width = int(rectangle[2])
       height = int(rectangle[3])
    +  
    +  if not os.path.exists(rsvgPath):
    +      print "Error: executable %s not found." % rsvgPath
         
       os.spawnlp(os.P_WAIT, rsvgPath, rsvgPath,
         "--source-rect=%g:%g:%g:%g" % (rectangle[0], rectangle[1], rectangle[2], rectangle[3]),
    @@ -115,6 +118,13 @@ def renderSvgIcon(file_path, out_dir):
       for rectangle in rectangles:
         renderSvgRsvg(file_path, out_dir, artwork_name, rectangle, doc_size)
     
    +def getTargetNames(file_path):
    +  """get a list of target names to be rendered from the given source SVG
    +     usable to setup the build targets for SCons
    +  """ 
    +  artwork_name, _ , rectangles = parseSVG(file_path)
    +  return ["%gx%g/%s.png" % (rectangle[2], rectangle[3], artwork_name) for rectangle in rectangles ]
    +
     #def renderSvgIcons():
     #  listing = os.listdir(svgDir)
     #  for file_path in listing:
    @@ -129,7 +139,8 @@ def renderSvgIcon(file_path, out_dir):
     #    copyMergeDirectory(src_dir, list_item)
     
     def printHelp():
    -  print "render-icon.py - An icon rendering utility script for lumiera"
    +  print "render-icon.py SRCFILE.svg TARGETDIR"
    +  print "An icon rendering utility script for lumiera"
     
     def parseArguments(argv):
       optlist, args = getopt.getopt(argv, "")
    diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py
    index 02f17b2b4..4b05166d1 100644
    --- a/admin/scons/Buildhelper.py
    +++ b/admin/scons/Buildhelper.py
    @@ -28,6 +28,8 @@ import fnmatch
     import re
     import tarfile
     
    +from SCons.Action import Action
    +
     
     
     #
    @@ -58,13 +60,13 @@ def srcSubtree(env,tree,isShared=False,builder=None, **args):
             else:
                 builder = lambda f: env.Object(f, **args)
             
    -    return [builder(f) for f in scanSrcSubtree(root)] 
    +    return [builder(f) for f in scanSubtree(root)] 
     
     
     
     SRCPATTERNS = ['*.c','*.cpp','*.cc']
     
    -def scanSrcSubtree(roots):
    +def scanSubtree(roots, patterns=SRCPATTERNS):
         """ first expand (possible) wildcards and filter out non-dirs. 
             Then scan the given subtree for source filesnames 
             (python generator function)
    @@ -73,7 +75,7 @@ def scanSrcSubtree(roots):
             for (dir,_,files) in os.walk(root):
                 if dir.startswith('./'):
                     dir = dir[2:]
    -            for p in SRCPATTERNS:
    +            for p in patterns:
                     for f in fnmatch.filter(files, p):
                         yield os.path.join(dir,f)
     
    @@ -102,6 +104,53 @@ def filterNodes(nlist, removeName=None):
         return filter(predicate, nlist)
     
     
    +
    +def getDirname(dir):
    +    """ extract directory name without leading path """
    +    dir = os.path.realpath(dir)
    +    if not os.path.isdir(dir):
    +        dir,_ = os.path.split(dir)
    +    _, name = os.path.split(dir)
    +    return name
    +    
    +
    +
    +def RegisterIcon_Builder(env, renderer):
    +    """ Registeres Custom Builders for generating and installing Icons.
    +        Additionally you need to build the tool (rsvg-convert.c)
    +        used to generate png from the svg source using librsvg. 
    +    """
    +    renderer = __import__(renderer) # load python script for invoking the render
    +    renderer.rsvgPath = env.subst("$BINDIR/rsvg-convert")
    +    
    +    def invokeRenderer(target, source, env):
    +        source = str(source[0])
    +        targetdir = env.subst("$BINDIR")
    +        renderer.main([source,targetdir])
    +        return 0
    +        
    +    def createIconTargets(target,source,env):
    +        """ parse the SVG to get the target file names """
    +        source = str(source[0])
    +        targetdir = os.path.basename(str(target[0]))
    +        targetfiles = renderer.getTargetNames(source)    # parse SVG
    +        return (["$BINDIR/%s" % name for name in targetfiles], source)
    +    
    +    def IconCopy(env, source):
    +         """Copy icon to corresponding icon dir. """
    +         subdir = getDirname(source)
    +         return env.Install("$BINDIR/%s" % subdir, source)
    +    
    +     
    +    buildIcon = env.Builder( action = Action(invokeRenderer, "rendering Icon: $SOURCE --> $TARGETS")
    +                           , single_source = True
    +                           , emitter = createIconTargets 
    +                           )
    +    env.Append(BUILDERS = {'IconRender' : buildIcon})    
    +    env.AddMethod(IconCopy)
    +
    +
    +
     def RegisterPrecompiledHeader_Builder(env):
         """ Registeres an Custom Builder for generating a precompiled Header.
             Note you should define a dependency to the PCH file
    diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py
    index 82d419a65..761d728cb 100644
    --- a/admin/scons/LumieraEnvironment.py
    +++ b/admin/scons/LumieraEnvironment.py
    @@ -77,10 +77,14 @@ class LumieraEnvironment(Environment):
             return libInfo
         
         def Glob (self, pattern):
    -        """ temporary workaround; newer versions of SCons provide this as a global function
    -        """
    +        """ temporary workaround; newer versions of SCons provide this as a global function """
             pattern = self.subst(pattern)
             return glob.glob(pattern)
    +    
    +    def AddMethod (self, function):
    +        """ temporary workaround; newer versions of SCons provide this as a global function """
    +        self.__dict__[function.__name__] = function.__get__(self)
    +    
     
     
     class LumieraConfigContext(SConf):
    diff --git a/wiki/compatibility.html b/wiki/compatibility.html
    index 5327731c0..56353ac46 100644
    --- a/wiki/compatibility.html
    +++ b/wiki/compatibility.html
    @@ -747,7 +747,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
     }
     //}}}
    -
    +
    ! Programming Languages
     * C
     ** a C99 compatible compiler, some GCC extensions are used, most are optional.
    @@ -787,7 +787,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
     ** libcairomm-1.0-dev (>=0.6.0)
     ** libgdl-1-dev (>=0.6.1)
     *** libbonoboui2-dev (>=2.14.0)
    -** libgtkmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
    +** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
     ** libxv-dev ~~(1.0.2 is known to work)~~
     
     
    diff --git a/wiki/index.html b/wiki/index.html
    index 55a65446f..c8a1a7ad2 100644
    --- a/wiki/index.html
    +++ b/wiki/index.html
    @@ -759,7 +759,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
     }
     //}}}
    -
    +
    for __Building__
     * gcc (4.1), glibc6 (2.3), libstdc++6 (4.1)
     * [[build system|BuildSystem]] dependencies: SCons (0.96.90), Python (2.4), pkg-config
    @@ -778,7 +778,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
     ** libcairomm-1.0-dev (>=0.6.0)
     ** libgdl-1-dev (>=0.6.1)
     *** libbonoboui2-dev (>=2.14.0)
    -** libgtkmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
    +** libglibmm-2.4-dev (>=2.16), requiring glib2.0 (>=2.16)
     ** libxv-dev (>=1.0.2)
     //usually, newer versions are OK//
     
    
    From 4746dcd233fc249449f02b0f4fc6978a21024f21 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Wed, 20 Aug 2008 15:45:36 +0100
    Subject: [PATCH 30/40] Fix build with gcc 4.3 (Explanation: 4.3 detects when a
     local typedef redeclares a templated type  from an enclosing scope. This was
     never legal anyway)
    
    ---
     .gitignore                                        |  2 ++
     src/common/meta/configflags.hpp                   | 15 +++++++++------
     src/proc/asset/buildinstruct.hpp                  | 10 +++++++++-
     src/proc/asset/procpatt.cpp                       |  3 ++-
     src/proc/engine/nodewiring.hpp                    |  2 +-
     src/proc/mobject/session/defsregistry.hpp         |  4 ++--
     .../proc/mobject/session/defsregistryimpltest.cpp |  4 ++--
     7 files changed, 27 insertions(+), 13 deletions(-)
    
    diff --git a/.gitignore b/.gitignore
    index a42a27923..69cb7933a 100644
    --- a/.gitignore
    +++ b/.gitignore
    @@ -2,6 +2,7 @@
     *~
     *.tar.*
     .[^.]*
    +*.o
     *.os
     *.gch
     ,valgrind.log*
    @@ -13,5 +14,6 @@ bin/*
     autom4te.cache/*
     scripts/*
     configure
    +config.log
     aclocal.m4
     semantic.cache
    diff --git a/src/common/meta/configflags.hpp b/src/common/meta/configflags.hpp
    index fa24bfc97..c63552855 100644
    --- a/src/common/meta/configflags.hpp
    +++ b/src/common/meta/configflags.hpp
    @@ -107,26 +107,29 @@ namespace lumiera {
                 >
         struct ConfigSetFlag >
           {
    -        typedef Config Config;
    +        typedef typelist::Config Config;
           };
         
         
         
    -    /** build a configuration type for the given list-of-flags */
    -    template >
    -    struct DefineConfigByFlags
    +    /** build a configuration type from a list-of-flags */
    +    template >
    +    struct BuildConfigFromFlags
           {
             typedef CONF Config;
             typedef Config Type;
           };
         template
    -    struct DefineConfigByFlags< Node,FLAGS>, CONF>
    +    struct BuildConfigFromFlags< Node,FLAGS>, CONF>
           { 
             typedef typename ConfigSetFlag< Fl
    -                                      , typename DefineConfigByFlags::Config
    +                                      , typename BuildConfigFromFlags::Config
                                           >::Config Config;
             typedef Config Type;
           };
    +    /** create a configuration type for the given list-of-flags */
    +    template
    +    struct DefineConfigByFlags : BuildConfigFromFlags { };    
         
         
         /** 
    diff --git a/src/proc/asset/buildinstruct.hpp b/src/proc/asset/buildinstruct.hpp
    index 8b1162103..dee85d58d 100644
    --- a/src/proc/asset/buildinstruct.hpp
    +++ b/src/proc/asset/buildinstruct.hpp
    @@ -108,7 +108,15 @@ namespace asset
         : public InstructEntry 
         {
           template
    -      BuildInstruct (T& instr) : InstructEntry(instr) {}
    +      BuildInstruct (T& instr) : InstructEntry() {}
    +
    +      // TODO: this ctor is *not* correct, just to make it compile
    +      // There is a strange problem with boost::variant, probably becausse the
    +      // template parameter T could be anything (but actually we know it's one
    +      // of our Instruction types.
    +      // I have to reinvestigate this design anyway, and probably will replace
    +      // the boost::variant by something else, derive from a common base or such.
    +      // Note: as of 8/2008 ProcPatt is just a draft and not implemented.
         };
         
         
    diff --git a/src/proc/asset/procpatt.cpp b/src/proc/asset/procpatt.cpp
    index d5173b9ea..5f2c842fd 100644
    --- a/src/proc/asset/procpatt.cpp
    +++ b/src/proc/asset/procpatt.cpp
    @@ -91,7 +91,8 @@ namespace asset
         else
           {
             DoAttach entry(node, where);
    -        instructions_.push_back(BuildInstruct(entry));
    +        BuildInstruct instruction(entry);
    +        instructions_.push_back(instruction);
           }
         TODO ("declare dependency??");
         return *this;
    diff --git a/src/proc/engine/nodewiring.hpp b/src/proc/engine/nodewiring.hpp
    index 442ca4297..6ad582efc 100644
    --- a/src/proc/engine/nodewiring.hpp
    +++ b/src/proc/engine/nodewiring.hpp
    @@ -37,7 +37,7 @@ namespace engine {
     
       class WiringFactory;
       
    -  namespace { class WiringFactoryImpl; }
    +  namespace { class WiringFactoryImpl; }  ////TODO: better use a named implementation namespace! (produces warings on gcc 4.3)
       
       
       /**
    diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp
    index 7d7589110..64f84ab32 100644
    --- a/src/proc/mobject/session/defsregistry.hpp
    +++ b/src/proc/mobject/session/defsregistry.hpp
    @@ -52,7 +52,7 @@ namespace mobject
         using boost::lambda::_1;
         using boost::lambda::var;  
         
    -    namespace // Implementation details
    +    namespace // Implementation details //////////////////TODO better a named implementation namespace (avoids warnings on gcc 4.3)
           {
           struct TableEntry 
             {
    @@ -159,7 +159,7 @@ namespace mobject
           template
           size_t Slot::index (0);
           
    -    }
    +    } // (End) impl namespace
         
     
         /**
    diff --git a/tests/components/proc/mobject/session/defsregistryimpltest.cpp b/tests/components/proc/mobject/session/defsregistryimpltest.cpp
    index e605880a1..e2c515600 100644
    --- a/tests/components/proc/mobject/session/defsregistryimpltest.cpp
    +++ b/tests/components/proc/mobject/session/defsregistryimpltest.cpp
    @@ -102,8 +102,8 @@ namespace mobject
             {
               scoped_ptr reg_;
               
    -          typedef P > O;
    -          typedef P > P;
    +          typedef lumiera::P > O;
    +          typedef lumiera::P > P;
               
               typedef Query > Q13;
               typedef Query > Q23;
    
    From 01fdb5e791fb11d5d733afc4a61aa622246e5301 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Wed, 20 Aug 2008 15:46:38 +0100
    Subject: [PATCH 31/40] automatically set DEBUG when using the (current) NoBug
     default ALPHA
    
    ---
     SConstruct | 3 +--
     1 file changed, 1 insertion(+), 2 deletions(-)
    
    diff --git a/SConstruct b/SConstruct
    index cbe45b1b3..f5b438944 100644
    --- a/SConstruct
    +++ b/SConstruct
    @@ -103,8 +103,7 @@ def handleNoBugSwitches(env):
         level = env['BUILDLEVEL']
         if level in ['ALPHA', 'BETA']:
             if not env['DEBUG']:
    -            print 'NoBug: ALPHA or BETA builds without DEBUG not possible, exiting.'
    -            Exit(1)
    +            print 'Warning: NoBug ALPHA or BETA builds requires DEBUG=yes, switching DEBUG on!'
             env.Replace( DEBUG = 1 )
             env.Append(CPPDEFINES = 'EBUG_'+level)
         elif level == 'RELEASE':
    
    From 944b7c4dd4f17703d4983fc95dd3ffb551db439a Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Wed, 20 Aug 2008 15:50:17 +0100
    Subject: [PATCH 32/40] preliminary fix for the SCons build for Debian/testing
     WARNING: breakes build on Debian/stable Explanation: I tried to provide a
     backported implementtation of features introduced with SCons 1.0 (until we
     can require SCons 1.0 officially) Unfortionately, some internal APIs changed
     quite large in 0.97 Have to build in some conditional code, so it works with
     SCons 0.96 again)
    
    ---
     SConstruct                        | 10 +++++-----
     admin/scons/LumieraEnvironment.py | 20 ++++++++++----------
     2 files changed, 15 insertions(+), 15 deletions(-)
    
    diff --git a/SConstruct b/SConstruct
    index f5b438944..3a77f0cce 100644
    --- a/SConstruct
    +++ b/SConstruct
    @@ -291,11 +291,11 @@ def defineBuildTargets(env, artifacts):
                 )
         
         # use PCH to speed up building
    -    precomp = ( env.PrecompiledHeader('$SRCDIR/pre')
    -              + env.PrecompiledHeader('$SRCDIR/pre_a')
    -              )
    -    env.Depends(objproc, precomp)
    -    env.Depends(objlib, precomp)
    +#    precomp = ( env.PrecompiledHeader('$SRCDIR/pre')
    +#              + env.PrecompiledHeader('$SRCDIR/pre_a')
    +#              )
    +#    env.Depends(objproc, precomp)
    +#    env.Depends(objlib, precomp)
         
         artifacts['lumiera'] = env.Program('$BINDIR/lumiera', ['$SRCDIR/main.cpp']+ core )
         artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', objplug)
    diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py
    index 761d728cb..8e39dd516 100644
    --- a/admin/scons/LumieraEnvironment.py
    +++ b/admin/scons/LumieraEnvironment.py
    @@ -22,7 +22,7 @@
     #####################################################################
     
     
    -from SCons.SConf       import SConf
    +from SCons.SConf       import SConfBase
     from SCons.Environment import Environment
     
     from Buildhelper import *
    @@ -76,22 +76,22 @@ class LumieraEnvironment(Environment):
             libInfo.ParseConfig ('pkg-config --cflags --libs '+ libID )
             return libInfo
         
    -    def Glob (self, pattern):
    -        """ temporary workaround; newer versions of SCons provide this as a global function """
    -        pattern = self.subst(pattern)
    -        return glob.glob(pattern)
    +#   def Glob (self, pattern):
    +#       """ temporary workaround; newer versions of SCons provide this as a global function """
    +#       pattern = self.subst(pattern)
    +#       return glob.glob(pattern)
         
    -    def AddMethod (self, function):
    -        """ temporary workaround; newer versions of SCons provide this as a global function """
    -        self.__dict__[function.__name__] = function.__get__(self)
    +#   def AddMethod (self, function):
    +#       """ temporary workaround; newer versions of SCons provide this as a global function """
    +#       self.__dict__[function.__name__] = function.__get__(self)
         
     
     
    -class LumieraConfigContext(SConf):
    +class LumieraConfigContext(SConfBase):
         """ Extends the SCons Configure context with some convenience methods
         """
         def __init__(self, *args,**kw):
    -        SConf.__init__(self,*args,**kw)
    +        SConfBase.__init__(self,*args,**kw)
         
         def CheckPkgConfig (self, libID, minVersion=0):
             print "Checking for library configuration: %s " % libID
    
    From 0ece037f9145b0c2a3465e7320836c6de6ee8187 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 21 Aug 2008 01:24:46 +0200
    Subject: [PATCH 33/40] make build work again with SCons 0.96 Added a
     conditional switch to use the pre-0.98-fix only for the old SCons versions
    
    ---
     admin/scons/LumieraEnvironment.py | 36 +++++++++++++++++++++----------
     1 file changed, 25 insertions(+), 11 deletions(-)
    
    diff --git a/admin/scons/LumieraEnvironment.py b/admin/scons/LumieraEnvironment.py
    index 8e39dd516..2d8f1bb01 100644
    --- a/admin/scons/LumieraEnvironment.py
    +++ b/admin/scons/LumieraEnvironment.py
    @@ -22,7 +22,8 @@
     #####################################################################
     
     
    -from SCons.SConf       import SConfBase
    +import SCons
    +import SCons.SConf
     from SCons.Environment import Environment
     
     from Buildhelper import *
    @@ -76,22 +77,35 @@ class LumieraEnvironment(Environment):
             libInfo.ParseConfig ('pkg-config --cflags --libs '+ libID )
             return libInfo
         
    -#   def Glob (self, pattern):
    -#       """ temporary workaround; newer versions of SCons provide this as a global function """
    -#       pattern = self.subst(pattern)
    -#       return glob.glob(pattern)
    -    
    -#   def AddMethod (self, function):
    -#       """ temporary workaround; newer versions of SCons provide this as a global function """
    -#       self.__dict__[function.__name__] = function.__get__(self)
    +    def Glob (self, pattern):
    +        """ temporary workaround; newer versions of SCons provide this as a global function """
    +        pattern = self.subst(pattern)
    +        return glob.glob(pattern)
         
    +    def AddMethod (self, function):
    +        """ temporary workaround; newer versions of SCons provide this as a global function """
    +        self.__dict__[function.__name__] = function.__get__(self)
    +        
    +        
    +#### temporary pre 1.0 SCons compatibility hack ####
    +_ver = map(int, SCons.__version__.split('.'))
    +_old = (_ver[0]<1 and _ver[1]<98)
    +if _old:
    +    ConfigBase = SCons.SConf.SConf
    +else:
    +    ConfigBase = SCons.SConf.SConfBase
    +    del LumieraEnvironment.Glob
    +    del LumieraEnvironment.AddMethod
    +    # use the official impl present since SCons 0.98
    +    # use the new name of the config context base class
     
     
    -class LumieraConfigContext(SConfBase):
    +
    +class LumieraConfigContext(ConfigBase):
         """ Extends the SCons Configure context with some convenience methods
         """
         def __init__(self, *args,**kw):
    -        SConfBase.__init__(self,*args,**kw)
    +        ConfigBase.__init__(self,*args,**kw)
         
         def CheckPkgConfig (self, libID, minVersion=0):
             print "Checking for library configuration: %s " % libID
    
    From 616d522ea93d06d9b6881a0ba43edcbf7a6f0572 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 21 Aug 2008 09:58:34 +0200
    Subject: [PATCH 34/40] yet more make fixes
    
    ---
     tests/common/Makefile.am     | 8 +++++---
     tests/components/Makefile.am | 3 ++-
     2 files changed, 7 insertions(+), 4 deletions(-)
    
    diff --git a/tests/common/Makefile.am b/tests/common/Makefile.am
    index cc027e477..f9f5867e5 100644
    --- a/tests/common/Makefile.am
    +++ b/tests/common/Makefile.am
    @@ -41,7 +41,7 @@ test_common_SOURCES =						\
     	$(testcommon_srcdir)/factorytest.cpp			\
     	$(testcommon_srcdir)/helloworldtest.cpp			\
     	$(testcommon_srcdir)/lifecycletest.cpp			\
    -	$(testcommon_srcdir)/mainsuite.cpp			\
    +	$(testcommon_srcdir)/mainsuite.cpp				\
     	$(testcommon_srcdir)/query/queryutilstest.cpp		\
     	$(testcommon_srcdir)/removefromsettest.cpp		\
     	$(testcommon_srcdir)/sanitizedidentifiertest.cpp	\
    @@ -50,8 +50,10 @@ test_common_SOURCES =						\
     	$(testcommon_srcdir)/singletontestmocktest.cpp		\
     	$(testcommon_srcdir)/test/cmdlinewrappertest.cpp	\
     	$(testcommon_srcdir)/test/testoptiontest.cpp		\
    -	$(testcommon_srcdir)/typelisttest.cpp			\
    -	$(testcommon_srcdir)/typelistutiltest.cpp		\
    +	$(testcommon_srcdir)/meta/typelisttest.cpp         	\
    +	$(testcommon_srcdir)/meta/typelismaniptest.cpp     	\
    +	$(testcommon_srcdir)/meta/generatortest.cpp        	\
    +	$(testcommon_srcdir)/meta/configflagstest.cpp      	\
     	$(testcommon_srcdir)/visitingtoolconcept.cpp		\
     	$(testcommon_srcdir)/visitingtoolextendedtest.cpp	\
     	$(testcommon_srcdir)/visitingtooltest.cpp
    diff --git a/tests/components/Makefile.am b/tests/components/Makefile.am
    index 088dbe668..3fce30365 100644
    --- a/tests/components/Makefile.am
    +++ b/tests/components/Makefile.am
    @@ -47,7 +47,8 @@ test_components_SOURCES =							\
     	$(testcomponents_srcdir)/proc/asset/makecliptest.cpp			\
     	$(testcomponents_srcdir)/proc/asset/orderingofassetstest.cpp		\
     	$(testcomponents_srcdir)/proc/asset/testasset.cpp			\
    -	$(testcomponents_srcdir)/proc/engine/sourcenodetest.cpp			\
    +	$(testcomponents_srcdir)/proc/engine/bufftabletest.cpp         	\
    +	$(testcomponents_srcdir)/proc/engine/sourcenodetest.cpp         	\
     	$(testcomponents_srcdir)/proc/mobject/builder/buildertooltest.cpp	\
     	$(testcomponents_srcdir)/proc/mobject/builder/buildsegmenttest.cpp	\
     	$(testcomponents_srcdir)/proc/mobject/controller/rendersegmenttest.cpp	\
    
    From 32dd0e661ae59be6dd3210c0cf8caeb1cacc1d3f Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 21 Aug 2008 09:59:24 +0200
    Subject: [PATCH 35/40] replace the homebrew GCH-builder by a lib solution
    
    ---
     SConstruct                 |  32 ++++++-----
     admin/scons/BuilderGCH.py  | 111 +++++++++++++++++++++++++++++++++++++
     admin/scons/Buildhelper.py |  19 -------
     3 files changed, 128 insertions(+), 34 deletions(-)
     create mode 100644 admin/scons/BuilderGCH.py
    
    diff --git a/SConstruct b/SConstruct
    index 3a77f0cce..421502159 100644
    --- a/SConstruct
    +++ b/SConstruct
    @@ -21,13 +21,6 @@
     #  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     #####################################################################
     
    -import sys
    -sys.path.append("./admin/scons")
    -
    -import os
    -from Buildhelper import *
    -from LumieraEnvironment import *
    -
     
     #-----------------------------------Configuration
     OPTIONSCACHEFILE = 'optcache' 
    @@ -37,6 +30,7 @@ BINDIR           = 'bin'
     TESTDIR          = 'tests'
     ICONDIR          = 'icons'
     VERSION          = '0.1+pre.01'
    +TOOLDIR          = './admin/scons'
     SVGRENDERER      = 'admin/render-icon'
     #-----------------------------------Configuration
     
    @@ -47,6 +41,14 @@ SVGRENDERER      = 'admin/render-icon'
     
     
     
    +import os
    +import sys
    +
    +sys.path.append(TOOLDIR)
    +
    +from Buildhelper import *
    +from LumieraEnvironment import *
    +
     
     #####################################################################
     
    @@ -57,7 +59,10 @@ def setupBasicEnvironment():
         EnsureSConsVersion(0,96,90)
         
         opts = defineCmdlineOptions() 
    -    env = LumieraEnvironment(options=opts) 
    +    env = LumieraEnvironment(options=opts
    +                            ,toolpath = [TOOLDIR]
    +                            ,tools = ["default", "BuilderGCH"]  
    +                            ) 
         
         env.Append ( CCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honor CFLAGS, only CCFLAGS 
         env.Replace( VERSION=VERSION
    @@ -70,7 +75,6 @@ def setupBasicEnvironment():
                    )
         
         RegisterIcon_Builder(env,SVGRENDERER)
    -    RegisterPrecompiledHeader_Builder(env)
         handleNoBugSwitches(env)
         
         env.Append(CPPDEFINES = '_GNU_SOURCE')
    @@ -278,6 +282,10 @@ def defineBuildTargets(env, artifacts):
             setup sub-environments with special build options if necessary.
             We use a custom function to declare a whole tree of srcfiles. 
         """
    +    # use PCH to speed up building
    +    env['GCH'] = ( env.PrecompiledHeader('$SRCDIR/pre.hpp')
    +                 + env.PrecompiledHeader('$SRCDIR/pre_a.hpp')
    +                 )
         
         objback =   srcSubtree(env,'$SRCDIR/backend') 
         objproc =   srcSubtree(env,'$SRCDIR/proc')
    @@ -290,12 +298,6 @@ def defineBuildTargets(env, artifacts):
                 + env.StaticLibrary('$BINDIR/lumi.la',     objlib)
                 )
         
    -    # use PCH to speed up building
    -#    precomp = ( env.PrecompiledHeader('$SRCDIR/pre')
    -#              + env.PrecompiledHeader('$SRCDIR/pre_a')
    -#              )
    -#    env.Depends(objproc, precomp)
    -#    env.Depends(objlib, precomp)
         
         artifacts['lumiera'] = env.Program('$BINDIR/lumiera', ['$SRCDIR/main.cpp']+ core )
         artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', objplug)
    diff --git a/admin/scons/BuilderGCH.py b/admin/scons/BuilderGCH.py
    new file mode 100644
    index 000000000..a8e0eb95c
    --- /dev/null
    +++ b/admin/scons/BuilderGCH.py
    @@ -0,0 +1,111 @@
    +# -*- python -*-
    +##
    +## BuilderGCH.py  -  SCons builder for gcc's precompiled headers
    +##
    +
    +#  Copyright (C)         scons.org/wiki/GchBuilder
    +#    2006                Tim Blechmann
    +#    2008                Hermann Vosseler 
    +#
    +#  This program is free software; you can redistribute it and/or
    +#  modify it under the terms of the GNU General Public License as
    +#  published by the Free Software Foundation; either version 2 of
    +#  the License, or (at your option) any later version.
    +#
    +#  This program is distributed in the hope that it will be useful,
    +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
    +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    +#  GNU General Public License for more details.
    +#
    +#  You should have received a copy of the GNU General Public License
    +#  along with this program; if not, write to the Free Software
    +#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    +#####################################################################
    +
    +# history: 8/2008 adapted for Lumiera build system
    +#                 changed to accept a list of precompiled header defs 
    +
    +
    +from types import ListType
    +
    +import SCons.Action
    +import SCons.Builder
    +import SCons.Scanner.C
    +import SCons.Util
    +import SCons.Script
    +
    +SCons.Script.EnsureSConsVersion(0,96,92)
    +
    +GchAction = SCons.Action.Action('$GCHCOM', '$GCHCOMSTR')
    +GchShAction = SCons.Action.Action('$GCHSHCOM', '$GCHSHCOMSTR')
    +
    +def gen_suffix(env, sources):
    +    return sources[0].get_suffix() + env['GCHSUFFIX']
    +
    +
    +GchShBuilder = SCons.Builder.Builder(action = GchShAction,
    +                                     source_scanner = SCons.Scanner.C.CScanner(),
    +                                     suffix = gen_suffix)
    +
    +GchBuilder = SCons.Builder.Builder(action = GchAction,
    +                                   source_scanner = SCons.Scanner.C.CScanner(),
    +                                   suffix = gen_suffix)
    +
    +def setup_dependency(target,source,env, key):
    +    scanner = SCons.Scanner.C.CScanner()
    +    path = scanner.path(env)
    +    deps = scanner(source[0], env, path)
    +
    +    if env.has_key(key) and env[key]:
    +        for header in env[key]:
    +            header_path = header.path.strip('.gch')
    +            if header_path in [x.path for x in deps]:
    +                print "Precompiled header(%s) %s  \t <--- %s" % (key,header_path,source[0])
    +                env.Depends(target, header)
    +    
    +
    +def static_pch_emitter(target,source,env):
    +    SCons.Defaults.StaticObjectEmitter( target, source, env )
    +    setup_dependency(target,source,env, key='GCH')
    +    return (target, source)
    +
    +def shared_pch_emitter(target,source,env):
    +    SCons.Defaults.SharedObjectEmitter( target, source, env )
    +    setup_dependency(target,source,env, key='GCH-sh')
    +    return (target, source)
    +
    +def generate(env):
    +    """
    +    Add builders and construction variables for the Gch builder.
    +    """
    +    env.Append(BUILDERS = {
    +        'gch': env.Builder(
    +        action = GchAction,
    +        target_factory = env.fs.File,
    +        ),
    +        'gchsh': env.Builder(
    +        action = GchShAction,
    +        target_factory = env.fs.File,
    +        ),
    +        })
    +
    +    try:
    +        bld   = env['BUILDERS']['GCH']
    +        bldsh = env['BUILDERS']['GCH-sh']
    +    except KeyError:
    +        bld = GchBuilder
    +        bldsh = GchShBuilder
    +        env['BUILDERS']['PrecompiledHeader'] = bld
    +        env['BUILDERS']['PrecompiledHeaderShared'] = bldsh
    +
    +    env['GCHCOM']     = '$CXX -o $TARGET -x c++-header -c $CXXFLAGS $_CCCOMCOM $SOURCE'
    +    env['GCHSHCOM']   = '$CXX -o $TARGET -x c++-header -c $SHCXXFLAGS $_CCCOMCOM $SOURCE'
    +    env['GCHSUFFIX']  = '.gch'
    +
    +    for suffix in SCons.Util.Split('.c .C .cc .cxx .cpp .c++'):
    +        env['BUILDERS']['StaticObject'].add_emitter( suffix, static_pch_emitter )
    +        env['BUILDERS']['SharedObject'].add_emitter( suffix, shared_pch_emitter )
    +
    +
    +def exists(env):
    +    return env.Detect('g++')
    diff --git a/admin/scons/Buildhelper.py b/admin/scons/Buildhelper.py
    index 4b05166d1..38940424c 100644
    --- a/admin/scons/Buildhelper.py
    +++ b/admin/scons/Buildhelper.py
    @@ -151,25 +151,6 @@ def RegisterIcon_Builder(env, renderer):
     
     
     
    -def RegisterPrecompiledHeader_Builder(env):
    -    """ Registeres an Custom Builder for generating a precompiled Header.
    -        Note you should define a dependency to the PCH file
    -    """
    -    def genCmdline(source, target, env, for_signature):
    -        return '$CXXCOM -x c++-header %s' % source[0]
    -    def fixSourceDependency(target, source, env):
    -        print "precompiled header: %s --> %s" % (source[0],target[0])
    -        return (target, source)
    -    
    -    gchBuilder = env.Builder( generator = genCmdline
    -                            , emitter = fixSourceDependency
    -                            , suffix = '.gch'
    -                            , src_suffix = '.hpp'
    -                            )
    -    env.Append(BUILDERS = {'PrecompiledHeader' : gchBuilder})    
    -
    -
    -
     def Tarball(env,location,dirs,suffix=''):
         """ Custom Command: create Tarball of some subdirs
             location: where to create the tar (may optionally include filename.tar.gz)
    
    From 29d2a73633473662aa1db49f70461b470f9522bf Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 21 Aug 2008 10:02:49 +0200
    Subject: [PATCH 36/40] disable precompiled headers for now. gcc fails, to be
     investigated later....
    
    ---
     SConstruct | 6 +++---
     1 file changed, 3 insertions(+), 3 deletions(-)
    
    diff --git a/SConstruct b/SConstruct
    index 421502159..93c3e39fd 100644
    --- a/SConstruct
    +++ b/SConstruct
    @@ -283,9 +283,9 @@ def defineBuildTargets(env, artifacts):
             We use a custom function to declare a whole tree of srcfiles. 
         """
         # use PCH to speed up building
    -    env['GCH'] = ( env.PrecompiledHeader('$SRCDIR/pre.hpp')
    -                 + env.PrecompiledHeader('$SRCDIR/pre_a.hpp')
    -                 )
    +#   env['GCH'] = ( env.PrecompiledHeader('$SRCDIR/pre.hpp')
    +#                + env.PrecompiledHeader('$SRCDIR/pre_a.hpp')
    +#                )
         
         objback =   srcSubtree(env,'$SRCDIR/backend') 
         objproc =   srcSubtree(env,'$SRCDIR/proc')
    
    From 840303fedf48132a3139ce0b15a9c2a8d1c8e923 Mon Sep 17 00:00:00 2001
    From: Christian Thaeter 
    Date: Sat, 23 Aug 2008 11:39:41 +0200
    Subject: [PATCH 37/40] makefile typo fix
    
    ---
     tests/common/Makefile.am | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/tests/common/Makefile.am b/tests/common/Makefile.am
    index f9f5867e5..7a985bec9 100644
    --- a/tests/common/Makefile.am
    +++ b/tests/common/Makefile.am
    @@ -51,7 +51,7 @@ test_common_SOURCES =						\
     	$(testcommon_srcdir)/test/cmdlinewrappertest.cpp	\
     	$(testcommon_srcdir)/test/testoptiontest.cpp		\
     	$(testcommon_srcdir)/meta/typelisttest.cpp         	\
    -	$(testcommon_srcdir)/meta/typelismaniptest.cpp     	\
    +	$(testcommon_srcdir)/meta/typelistmaniptest.cpp     	\
     	$(testcommon_srcdir)/meta/generatortest.cpp        	\
     	$(testcommon_srcdir)/meta/configflagstest.cpp      	\
     	$(testcommon_srcdir)/visitingtoolconcept.cpp		\
    
    From 6d5cf0e6424b2e9e428f211b15cafc491500abca Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 28 Aug 2008 02:57:12 +0200
    Subject: [PATCH 38/40] scons: use Doxygen builder (lib implementation from
     http://www.scons.org/wiki/DoxygenBuilder)
    
    ---
     SConstruct                    |  15 +--
     admin/scons/BuilderDoxygen.py | 230 ++++++++++++++++++++++++++++++++++
     admin/scons/BuilderGCH.py     |   3 +-
     doc/devel/.gitignore          |   1 +
     doc/devel/Doxyfile            |  10 +-
     5 files changed, 241 insertions(+), 18 deletions(-)
     create mode 100644 admin/scons/BuilderDoxygen.py
    
    diff --git a/SConstruct b/SConstruct
    index 93c3e39fd..d0a143db5 100644
    --- a/SConstruct
    +++ b/SConstruct
    @@ -61,7 +61,7 @@ def setupBasicEnvironment():
         opts = defineCmdlineOptions() 
         env = LumieraEnvironment(options=opts
                                 ,toolpath = [TOOLDIR]
    -                            ,tools = ["default", "BuilderGCH"]  
    +                            ,tools = ["default", "BuilderGCH", "BuilderDoxygen"]  
                                 ) 
         
         env.Append ( CCCOM=' -std=gnu99') # workaround for a bug: CCCOM currently doesn't honor CFLAGS, only CCFLAGS 
    @@ -340,16 +340,9 @@ def definePostBuildTargets(env, artifacts):
         env.Clean ('build', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log' ])
         env.Clean ('build', [ '$SRCDIR/pre.gch' ])
         
    -    # Doxygen documentation
    -    # Note: at the moment we only depend on Doxyfile
    -    #       obviousely, we should depend on all sourcefiles
    -    #       real Doxygen builder for scons is under developement for 0.97
    -    #       so for the moment I prefere not to bother
    -    doxyfile = File('doc/devel/Doxyfile')
    -    env.NoClean(doxyfile)
    -    doxydoc = artifacts['doxydoc'] = [ Dir('doc/devel/html'), Dir('doc/devel/latex') ]
    -    env.Command(doxydoc, doxyfile, "doxygen Doxyfile 2>&1 |tee ,doxylog",  chdir='doc/devel')
    -    env.Clean ('doc/devel', doxydoc + ['doc/devel/,doxylog'])
    +    doxydoc = artifacts['doxydoc'] = env.Doxygen('doc/devel/Doxyfile')
    +    env.Alias ('doxydoc', doxydoc)
    +    env.Clean ('doxydoc', doxydoc + ['doc/devel/,doxylog','doc/devel/warnings.txt'])
     
     
     def defineInstallTargets(env, artifacts):
    diff --git a/admin/scons/BuilderDoxygen.py b/admin/scons/BuilderDoxygen.py
    new file mode 100644
    index 000000000..943fce946
    --- /dev/null
    +++ b/admin/scons/BuilderDoxygen.py
    @@ -0,0 +1,230 @@
    +# -*- python -*-
    +##
    +## BuilderDoxygen.py  -  SCons builder for generating Doxygen documentation
    +##
    +
    +#
    +#  Astxx, the Asterisk C++ API and Utility Library.
    +#  Copyright (C) 2005, 2006  Matthew A. Nicholson
    +#  Copyright (C) 2006  Tim Blechmann
    +#
    +#  This library is free software; you can redistribute it and/or
    +#  modify it under the terms of the GNU Lesser General Public
    +#  License version 2.1 as published by the Free Software Foundation.
    +#
    +#  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/
    +#####################################################################
    +
    +# history: 8/2008 adapted for Lumiera build system
    +#                 added patch for Doxyfile in subdirectory 
    +#                 see http://www.scons.org/wiki/DoxygenBuilder 
    +
    +
    +import os
    +import os.path
    +import glob
    +from fnmatch import fnmatch
    +
    +
    +def DoxyfileParse(file_contents):
    +   """
    +   Parse a Doxygen source file and return a dictionary of all the values.
    +   Values will be strings and lists of strings.
    +   """
    +   data = {}
    +
    +   import shlex
    +   lex = shlex.shlex(instream = file_contents, posix = True)
    +   lex.wordchars += "*+./-:"
    +   lex.whitespace = lex.whitespace.replace("\n", "")
    +   lex.escape = ""
    +
    +   lineno = lex.lineno
    +   token = lex.get_token()
    +   key = token   # the first token should be a key
    +   last_token = ""
    +   key_token = False
    +   next_key = False
    +   new_data = True
    +
    +   def append_data(data, key, new_data, token):
    +      if new_data or len(data[key]) == 0:
    +         data[key].append(token)
    +      else:
    +         data[key][-1] += token
    +
    +   while token:
    +      if token in ['\n']:
    +         if last_token not in ['\\']:
    +            key_token = True
    +      elif token in ['\\']:
    +         pass
    +      elif key_token:
    +         key = token
    +         key_token = False
    +      else:
    +         if token == "+=":
    +            if not data.has_key(key):
    +               data[key] = list()
    +         elif token == "=":
    +            data[key] = list()
    +         else:
    +            append_data( data, key, new_data, token )
    +            new_data = True
    +
    +      last_token = token
    +      token = lex.get_token()
    +      
    +      if last_token == '\\' and token != '\n':
    +         new_data = False
    +         append_data( data, key, new_data, '\\' )
    +
    +   # compress lists of len 1 into single strings
    +   for (k, v) in data.items():
    +      if len(v) == 0:
    +         data.pop(k)
    +
    +      # items in the following list will be kept as lists and not converted to strings
    +      if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS"]:
    +         continue
    +
    +      if len(v) == 1:
    +         data[k] = v[0]
    +
    +   return data
    +
    +def DoxySourceScan(node, env, path):
    +   """
    +   Doxygen Doxyfile source scanner.  This should scan the Doxygen file and add
    +   any files used to generate docs to the list of source files.
    +   """
    +   default_file_patterns = [
    +      '*.c', '*.cc', '*.cxx', '*.cpp', '*.c++', '*.java', '*.ii', '*.ixx',
    +      '*.ipp', '*.i++', '*.inl', '*.h', '*.hh ', '*.hxx', '*.hpp', '*.h++',
    +      '*.idl', '*.odl', '*.cs', '*.php', '*.php3', '*.inc', '*.m', '*.mm',
    +      '*.py',
    +   ]
    +
    +   default_exclude_patterns = [
    +      '*~',
    +   ]
    +
    +   sources = []
    +
    +   data = DoxyfileParse(node.get_contents())
    +
    +   if data.get("RECURSIVE", "NO") == "YES":
    +      recursive = True
    +   else:
    +      recursive = False
    +
    +   file_patterns = data.get("FILE_PATTERNS", default_file_patterns)
    +   exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns)
    +   
    +   #
    +   # We're running in the top-level directory, but the doxygen
    +   # configuration file is in the same directory as node; this means
    +   # that relative pathnames in node must be adjusted before they can
    +   # go onto the sources list
    +   conf_dir = os.path.dirname(str(node))
    +
    +   for node in data.get("INPUT", []):
    +      if not os.path.isabs(node):
    +         node = os.path.join(conf_dir, node)
    +      if os.path.isfile(node):
    +         sources.append(node)
    +      elif os.path.isdir(node):
    +         if recursive:
    +            for root, dirs, files in os.walk(node):
    +               for f in files:
    +                  filename = os.path.join(root, f)
    +
    +                  pattern_check = reduce(lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False)
    +                  exclude_check = reduce(lambda x, y: x and fnmatch(filename, y), exclude_patterns, True)
    +
    +                  if pattern_check and not exclude_check:
    +                     sources.append(filename)
    +         else:
    +            for pattern in file_patterns:
    +               sources.extend(glob.glob("/".join([node, pattern])))
    +
    +   sources = map( lambda path: env.File(path), sources )
    +   return sources
    +
    +
    +def DoxySourceScanCheck(node, env):
    +   """Check if we should scan this file"""
    +   return os.path.isfile(node.path)
    +
    +def DoxyEmitter(source, target, env):
    +   """Doxygen Doxyfile emitter"""
    +   # possible output formats and their default values and output locations
    +   output_formats = {
    +      "HTML": ("YES", "html"),
    +      "LATEX": ("YES", "latex"),
    +      "RTF": ("NO", "rtf"),
    +      "MAN": ("NO", "man"),
    +      "XML": ("NO", "xml"),
    +   }
    +
    +   data = DoxyfileParse(source[0].get_contents())
    +
    +   targets = []
    +   out_dir = data.get("OUTPUT_DIRECTORY", ".")
    +
    +   # add our output locations
    +   for (k, v) in output_formats.items():
    +      if data.get("GENERATE_" + k, v[0]) == "YES":
    +         targets.append(env.Dir( os.path.join(out_dir, data.get(k + "_OUTPUT", v[1]))) )
    +
    +   # don't clobber targets
    +   for node in targets:
    +      env.Precious(node)
    +
    +   # set up cleaning stuff
    +   for node in targets:
    +      env.Clean(node, node)
    +
    +   return (targets, source)
    +
    +
    +def generate(env):
    +   """
    +   Add builders and construction variables for the
    +   Doxygen tool.  This is currently for Doxygen 1.4.6.
    +   """
    +   doxyfile_scanner = env.Scanner(
    +      DoxySourceScan,
    +      "DoxySourceScan",
    +      scan_check = DoxySourceScanCheck,
    +   )
    +
    +   import SCons.Builder
    +   doxyfile_builder = SCons.Builder.Builder(
    +      action = "cd ${SOURCE.dir}  &&  (${DOXYGEN} ${SOURCE.file} 2>&1 |tee ,doxylog)",
    +      emitter = DoxyEmitter,
    +      target_factory = env.fs.Entry,
    +      single_source = True,
    +      source_scanner =  doxyfile_scanner,
    +   )
    +
    +   env.Append(BUILDERS = {
    +      'Doxygen': doxyfile_builder,
    +   })
    +
    +   env.AppendUnique(
    +      DOXYGEN = 'doxygen',
    +   )
    +
    +def exists(env):
    +   """
    +   Make sure doxygen exists.
    +   """
    +   return env.Detect("doxygen")
    +
    diff --git a/admin/scons/BuilderGCH.py b/admin/scons/BuilderGCH.py
    index a8e0eb95c..c522cb9cd 100644
    --- a/admin/scons/BuilderGCH.py
    +++ b/admin/scons/BuilderGCH.py
    @@ -18,8 +18,7 @@
     #  GNU General Public License for more details.
     #
     #  You should have received a copy of the GNU General Public License
    -#  along with this program; if not, write to the Free Software
    -#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    +#  along with this program; if not, see http://www.gnu.org/licenses/
     #####################################################################
     
     # history: 8/2008 adapted for Lumiera build system
    diff --git a/doc/devel/.gitignore b/doc/devel/.gitignore
    index 96886436c..d6d649abd 100644
    --- a/doc/devel/.gitignore
    +++ b/doc/devel/.gitignore
    @@ -1,2 +1,3 @@
     ,doxylog
    +warnings.txt
     html/*
    diff --git a/doc/devel/Doxyfile b/doc/devel/Doxyfile
    index 28bab1f05..b7c59fa54 100644
    --- a/doc/devel/Doxyfile
    +++ b/doc/devel/Doxyfile
    @@ -5,7 +5,7 @@
     #---------------------------------------------------------------------------
     DOXYFILE_ENCODING      = UTF-8
     PROJECT_NAME           = Lumiera
    -PROJECT_NUMBER         = 3.0+alpha
    +PROJECT_NUMBER         = 0.1+pre
     OUTPUT_DIRECTORY       = 
     CREATE_SUBDIRS         = YES
     OUTPUT_LANGUAGE        = English
    @@ -32,7 +32,7 @@ SHORT_NAMES            = NO
     JAVADOC_AUTOBRIEF      = YES
     QT_AUTOBRIEF           = NO
     MULTILINE_CPP_IS_BRIEF = NO
    -DETAILS_AT_TOP         = NO
    +DETAILS_AT_TOP         = YES
     INHERIT_DOCS           = YES
     SEPARATE_MEMBER_PAGES  = NO
     TAB_SIZE               = 4
    @@ -65,8 +65,8 @@ CASE_SENSE_NAMES       = YES
     HIDE_SCOPE_NAMES       = NO
     SHOW_INCLUDE_FILES     = YES
     INLINE_INFO            = YES
    -SORT_MEMBER_DOCS       = YES
    -SORT_BRIEF_DOCS        = YES
    +SORT_MEMBER_DOCS       = NO
    +SORT_BRIEF_DOCS        = NO
     SORT_GROUP_NAMES       = NO
     SORT_BY_SCOPE_NAME     = NO
     GENERATE_TODOLIST      = YES
    @@ -179,7 +179,7 @@ HTML_STYLESHEET        =
     HTML_ALIGN_MEMBERS     = YES
     GENERATE_HTMLHELP      = NO
     GENERATE_DOCSET        = NO
    -DOCSET_FEEDNAME        = "Doxygen generated docs"
    +DOCSET_FEEDNAME        = "Lumiera Doxygen docs"
     DOCSET_BUNDLE_ID       = org.doxygen.Project
     HTML_DYNAMIC_SECTIONS  = NO
     CHM_FILE               = 
    
    From 699966c8207b8d43776a8d7ac948d2c7061fdb44 Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 28 Aug 2008 03:52:43 +0200
    Subject: [PATCH 39/40] add my favorite lumiera logo to the TiddlyWiki page
    
    ---
     wiki/draw/LumiLogo.png | Bin 0 -> 2345 bytes
     wiki/index.html        |  12 ++++++------
     2 files changed, 6 insertions(+), 6 deletions(-)
     create mode 100644 wiki/draw/LumiLogo.png
    
    diff --git a/wiki/draw/LumiLogo.png b/wiki/draw/LumiLogo.png
    new file mode 100644
    index 0000000000000000000000000000000000000000..1fd263aed1c9fc761b34bd79b337eaffcee5b004
    GIT binary patch
    literal 2345
    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
     
    -
    -
    This is the entry point to several [[TiddlyWiki]]-Pages containing the developer and design documentation for Lumiera.
    +
    +
    [<img[draw/LumiLogo.png]]
     
    -* Cehteh started GitNotes where we will collect some information about git, howto and special setups
    -* since we prefer gpg signed tags in git and not each developer knows gpg well here is a Micro-GPG-HowTo 
    +
    +  ''Lumiera'' is the emerging professional non linear video editor for Linux
    +
    +This is the entry point to several [[TiddlyWiki]]-Pages containing the developer and design documentation.
     * we maintain (semi-) final design docs in DesignDocumentation
     * Things get often worked out on IRC, see IRC-Transcripts for decisions made there and not yet put into the proper documentation places
     
    -Please __end your tiddlers in a newline__, this makes merging in git easier since the /pre tag used in tiddlywiki will become on a single line.
    -
     ----
     !Architecture and Subsystems
     to get started, we create design drafts emphasizing different aspects and regions of Lumiera
    
    From fc6cbcf8e4e04189a3fc52296392f41667bc12bd Mon Sep 17 00:00:00 2001
    From: Ichthyostega 
    Date: Thu, 28 Aug 2008 03:53:46 +0200
    Subject: [PATCH 40/40] improved the scons build to report all missing
     dependencies instead of stopping at the first one
    
    ---
     SConstruct | 60 +++++++++++++++++++++++++-----------------------------
     1 file changed, 28 insertions(+), 32 deletions(-)
    
    diff --git a/SConstruct b/SConstruct
    index d0a143db5..2b3e2cfd0 100644
    --- a/SConstruct
    +++ b/SConstruct
    @@ -173,24 +173,21 @@ def configurePlatform(env):
         # run all configuration checks in the given env
         
         # Perform checks for prerequisites --------------------------------------------
    +    problems = []
         if not conf.TryAction('pkg-config --version > $TARGET')[0]:
    -        print 'We need pkg-config for including library configurations, exiting.'
    -        Exit(1)
    +        problems.append('We need pkg-config for including library configurations, exiting.')
         
         if not conf.CheckLibWithHeader('m', 'math.h','C'):
    -        print 'Did not find math.h / libm, exiting.'
    -        Exit(1)
    +        problems.append('Did not find math.h / libm.')
         
         if not conf.CheckLibWithHeader('dl', 'dlfcn.h', 'C'):
    -        print 'Functions for runtime dynamic loading not available, exiting.'
    -        Exit(1)
    +        problems.append('Functions for runtime dynamic loading not available.')
         
         if not conf.CheckLibWithHeader('nobugmt', 'nobug.h', 'C'):
    -        print 'Did not find NoBug [http://www.pipapo.org/pipawiki/NoBug], exiting.'
    -        Exit(1)
    +        problems.append('Did not find NoBug [http://www.pipapo.org/pipawiki/NoBug].')
         
         if not conf.CheckLibWithHeader('pthread', 'pthread.h', 'C'):
    -        print 'Did not find the pthread lib or pthread.h, exiting.'
    +        problems.append('Did not find the pthread lib or pthread.h.')
         else:
            conf.env.Append(CPPFLAGS = ' -DHAVE_PTHREAD')
            conf.env.Append(CCFLAGS = ' -pthread')
    @@ -202,57 +199,56 @@ def configurePlatform(env):
             conf.env.Append(CPPFLAGS = ' -DHAS_VALGRIND_VALGIND_H')
         
         if not conf.CheckCXXHeader('tr1/memory'):
    -        print 'We rely on the std::tr1 proposed standard extension for shared_ptr.'
    -        Exit(1) 
    +        problems.append('We rely on the std::tr1 proposed standard extension for shared_ptr.')
         
         if not conf.CheckCXXHeader('boost/config.hpp'):
    -        print 'We need the C++ boost-lib.'
    -        Exit(1)
    +        problems.append('We need the C++ boost-lib.')
         else:
             if not conf.CheckCXXHeader('boost/shared_ptr.hpp'):
    -            print 'We need boost::shared_ptr (shared_ptr.hpp).'
    -            Exit(1)
    +            problems.append('We need boost::shared_ptr (shared_ptr.hpp).')
             if not conf.CheckLibWithHeader('boost_program_options-mt','boost/program_options.hpp','C++'):
    -            print 'We need boost::program_options (including binary lib for linking).'
    -            Exit(1)
    +            problems.append('We need boost::program_options (including binary lib for linking).')
             if not conf.CheckLibWithHeader('boost_regex-mt','boost/regex.hpp','C++'):
    -            print 'We need the boost regular expression lib (incl. binary lib for linking).'
    -            Exit(1)
    +            problems.append('We need the boost regular expression lib (incl. binary lib for linking).')
         
     #    if not conf.CheckLibWithHeader('gavl', ['gavlconfig.h', 'gavl/gavl.h'], 'C'):
         
         if not conf.CheckPkgConfig('gavl', 1.0):
    -        print 'Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html], exiting.'
    -        Exit(1)
    +        problems.append('Did not find Gmerlin Audio Video Lib [http://gmerlin.sourceforge.net/gavl.html].')
         else:
             conf.env.mergeConf('gavl')
         
         if not conf.CheckPkgConfig('gtkmm-2.4', 2.8):
    -        print 'Unable to configure GTK--, exiting.'
    -        Exit(1)
    +        problems.append('Unable to configure GTK--, exiting.')
             
         if not conf.CheckPkgConfig('glibmm-2.4', '2.16'):
    -        print 'Unable to configure Lib glib--, exiting.'
    -        Exit(1)
    +        problems.append('Unable to configure Lib glib--, exiting.')
         
         if not conf.CheckPkgConfig('cairomm-1.0', 0.6):
    -        print 'Unable to configure Cairo--, exiting.'
    -        Exit(1)
    +        problems.append('Unable to configure Cairo--, exiting.')
         
         if not conf.CheckPkgConfig('gdl-1.0', '0.6.1'):
    -        print 'Unable to configure the GNOME DevTool Library, exiting.'
    -        Exit(1)
    +        problems.append('Unable to configure the GNOME DevTool Library, exiting.')
         
         if not conf.CheckPkgConfig('librsvg-2.0', '2.18.1'):
    -        print 'Need rsvg Library for rendering icons.'
    -        Exit(1)
    +        problems.append('Need rsvg Library for rendering icons.')
             
    -    if not conf.CheckPkgConfig('xv'): Exit(1)
    +    if not conf.CheckPkgConfig('xv'): problems.append('Need lib xv')
     #   if not conf.CheckPkgConfig('xext'): Exit(1)
     #   if not conf.CheckPkgConfig('sm'): Exit(1)
     #    
     # obviously not needed?
         
    +    
    +    # report missing dependencies
    +    if problems:
    +        print "*** unable to build due to the following problems:"
    +        for isue in problems:
    +            print " *  %s" % isue
    +        print
    +        print "build aborted."
    +        Exit(1)
    +    
         print "** Gathered Library Info: %s" % conf.env.libInfo.keys()