Improving the compositor framework

Discussion area about developing or extending OGRE, adding plugins for it or building applications on it. No newbie questions please, use the Help forum for that.
Post Reply
User avatar
Noman
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 714
Joined: Mon Jan 31, 2005 7:21 pm
Location: Israel
Contact:

Improving the compositor framework

Post by Noman » Mon Jan 07, 2008 4:49 pm

[edit]
First version ready! http://nomanogre.googlepages.com/CompositorLogic.zip
[/edit]

Hello

I'm working on adding dynamic compositors to our product. Theoretically, Ogre supports loading compositors dynamically at runtime, but this is not entirely true. Even in the compositor example, some specific hard coded code had to be added in order to get some of the compositors to work (HDR, Gaussian Blur and Heat Vision). This is because they need to set the parameters of the GPU programs at runtime, according to the specific scene & environment.

I would like to create an infrasturcture in which even shaders like these can be added to a program without changing its code, or even recompiling it.

The solution - a unified Compositor logic API which will be able to load modules from files.

The first thing that needs to be done is to create a class that unifies the compositor and the surrounding logic. I called it CompositorLogic :

Code: Select all

enum ChainPosition {
	CP_NOT_IMPORTANT,
	CP_FIRST,
	CP_LAST
};

class CompositorLogic {

public:
	virtual ChainPosition getChainPosition() { return CP_NOT_IMPORTANT; }

	//For the use of the compositor to prepare its parameters and attach
	//listeners if necessary.
	virtual void notifyRenderTarget(Ogre::RenderTarget* renderTarget) {}
	virtual void notifyCompositor(Ogre::CompositorInstance* instance) {}
};
I'm not 100% sure that this API makes everything that a compositor writer would possibly like to do possible, but it covers everything found in the samples. You can register a listener in notifyCompositor, and get the viewport dimensions from the render target in notifyRenderTarget etc. Note that one CompositorLogic is created per instance of the compositor.

Once this is done, a manager class needs to manage the usage of these logics. The API will look something like this :

Code: Select all

class CompositorLogicManager : public Ogre::Singleton<CompositorLogicManager> {
public:
	std::vector<Ogre::String> getAvailableCompositors();

	std::vector<Ogre::String> getAttachedCompositors(RenderTarget* rt);
	void attachCompositor(RenderTarget* rt, const Ogre::String& compositor);
	void detachCompositor(RenderTarget* rt, const Ogre::String& compositor);

	void registerCompositorLogic(CompositorLogicFactory* newCompositor);
	void registerCompositorLogic(const Ogre::String& newCompositor);
};
The string overload of registerCompositorLogic is used to simplify the process of adding compositors that require no code (just scripts) by simply entering their name. A SimpleCompositorLogic class will be created wrapping the compositor and exposing it to the new API.

With the CompositorLogicFactory being a simple factory class :

Code: Select all

class CompositorLogicFactory {
public:
	virtual void initializeFactory() {}
	virtual CompositorLogic* createCompositorLogic() = 0;
	virtual const Ogre::String& getCompositorName() = 0;
};
Things like code-created compositors (rather than script) can be done in initializeFactory. Most compositors won't need to do anything there, which is the reason for there to be a default empty implimentation.

The CompositorLogic manager will be in charge of finding the compositor, attaching it to the render target and calling its functions in order to allow each instance to set its parameters according to whatever it needs.

Besides that, it will also perform some runtime checks to determine conflicts. For example, if two compositors both need to be the first compositor in the chain, they can not be used together. The CompositorLogicManager can spot these problems and warn or crash.

It will be possible to load the factories dynamically from DLLs and have them register themselves with the manager and then be used at runtime. Perhaps a slightly different exension should be used, to differentiate compositor plugins from other plugins and put them in the same family. This will make it easier to load them when ogre starts up. (Of course, manual creation and registration via code will still be possible).

This solution will make compositors (even more advanced one) truly dynamic and reusable.

To illustrate what this new API will look like, here is an example of modifying the compositor sample to work with this API. I will port the HDR compositor to this new framework. After moving the HDRListener class from the compositor example to a different file, i create this class :

HDRLogic.h

Code: Select all

#ifndef _HDRLogic_H_
#define _HDRLogic_H_

#include "CompositorLogic.h"
#include "HDRListener.h"

const Ogre::String HDR_LOGIC_NAME = "HDR";

class HDRLogic : public CompositorLogic {
public:
	virtual void getChainPosition() { return CP_FIRST; }
	
	virtual void notifyRenderTarget(Ogre::RenderTarget* renderTarget);
	virtual void notifyCompositor(Ogre::CompositorInstance* instance);
private:
	HDRListener mListener;
};

public class HDRLogicFactory : public CompositorLogicFactory {
public:
	
	virtual CompositorLogic* createCompositorLogic() { return new HDRLogic(); }
	virtual const Ogre::String& getCompositorName() { return HDR_LOGIC_NAME; }
};

#endif
HDRListener.cpp

Code: Select all

#include "HDRLogic.h"

#include <OgreRenderTarget.h>

void HDRLogic::notifyRenderTarget(Ogre::RenderTarget* renderTarget) 
{
	mListener.notifyViewportSize(renderTarget->getWidth(), renderTarget->getHeight());
}

void HDRLogic::notifyCompositor(Ogre::CompositorInstance* instance)
{
	mListener.notifyCompositor(instance);
	instance->addListener(&mListener);
}
This needs to be compiled into a DLL, which will be loaded as a plugin and registered with the compositor logic manager. If it isn't then I call the function :

Code: Select all

CompositorLogicManager::getSingleton().registerCompositorLogic(new HDRLogicFactory());
After that, all I need to do in order to enable the compositor is call

Code: Select all

CompositorLogicManager::getSingleton().attachCompositor("HDR", mViewport->getTarget());
And the compositor will be added, including running the code that generates the correct parameters based on the viewport size, and the listener will be registered.


I will implement this solution anyway, but thought about consulting the community because i think that you might also be interested in it, and I will be able to submit this as a patch to be added to ogre.

I have some questions, and would like to hear your thoughts (in general, but about them as well) :
  • Is the CompositorLogic API enough? Did you create any compositors that don't get all the information they need to set themselves up from this API?
  • Is the ChainPosition enum enough? Will people want more fine-grained control over the ordering?
  • Do you have any suggestions for the names of the classes? Perhaps CompositorLogicInstance+CompositorLogic instead of CompositorLogic+CompositorLogicFactory (to be in sync with Ogre's CompositorInstance+Compositor) ? Is CompositorLogic a fitting name?
I would be glad to get as much input from the community as possible before implementing this.

Thanks!
Last edited by Noman on Sun Jan 13, 2008 2:47 pm, edited 1 time in total.
0 x

User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19261
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
Contact:

Post by sinbad » Tue Jan 08, 2008 1:22 pm

Thanks for the thought-provoking post, you have a valid point about it being beneficial to have a better way to package up the compositors which need listeners.

The way it's presented here though, I'm not sure I see it as a core feature, more like a samples helper class, much like other helpers we have in the Wiki. The trouble with a code-based approach is that it still doesn't solve the disconnect between scripts (.compositor and the materials and shaders it uses) and the supplementary code required. Your solution packages up the code parts better for sure, but the disconnect is still there - you still need both the scripts and the C++ implementations, which is why I don't see it as a core solution yet.

Now, if you showed me something that made it possible to do the HDR compositor entirely in script, that would be something - only then would you have a completely media-deployable version of these more complex compositors. This is all about abstracting the procedural aspects into some declarative interface that can be used in a script, with all the issues about generality that entails. I'm not expecting you to do this, especially if your helper classes do what you need, I'm just saying that's really what I'd be looking for in a core enhancement.

To reiterate though, I'm not devaluing your ideas here, they're very valid and would be useful to some people I'm sure, but as sample / app helper classes in the Wiki rather than a core feature, I think.
0 x

User avatar
Assaf Raman
OGRE Team Member
OGRE Team Member
Posts: 3092
Joined: Tue Apr 11, 2006 3:58 pm
Location: TLV, Israel

Post by Assaf Raman » Tue Jan 08, 2008 1:55 pm

I agree with Noman that there is a real need for this, I think it will make order in the architecture. Moreover – I think that after Noman will finish his work – it will be much easier to sure more complex compositors in the forum and it will be easier for ppl (personally me) to integrate new compositors to my project.

I guess Sinbad has a point in the sense that when you compile C++ code – you need to re-compile for linux\win32\mac and that makes this kind of resource more complex to use then other resources like meshs and materials and such.

Personally I like Noman's approach – of C++ code - much better then the one Sinbad is taking – "HDR compositor entirely in script" – I think there are so much options for compositor logics – and only c++ code is the solution for the more advanced shaders needed in applications.

I recommend continuing in this manner – Noman will finish his code, when done – he will present it in the showcase forum with a download for an exe demo and the source. I think that his offer can replace the current compositor demo – this way everybody will get his code as part of the basic code – but it will not be a real part of the "core", it will be more as the sample how to have advanced compositors in a plug-in architecture. We can vote in a pull later and decided.

I think that a plug-in architecture if a good idea for the compositors and I think that if the sample will come with the basic code of OGRE – it will encourage more ppl to share there advanced compositors in an easy to reuse manner.
0 x
Watch out for my OGRE related tweets here.

User avatar
Game_Ender
Ogre Magi
Posts: 1269
Joined: Wed May 25, 2005 2:31 am
Location: Rockville, MD, USA

Post by Game_Ender » Tue Jan 08, 2008 5:26 pm

Could this not be some sort of "Compositor Enhancement Plugin"? I think that will see much more exposure than a wiki page. As an Ogre plugin it can be properly packaged and documented so people can get better use out of it.
0 x

User avatar
Praetor
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 3335
Joined: Tue Jun 21, 2005 8:26 pm
Location: Rochester, New York, US
Contact:

Post by Praetor » Tue Jan 08, 2008 8:05 pm

I'd like to see it implemented. It sounds intriguing at least. I think it will easier to evaluate when we can see it. But Noman, I think you have good ideas. Let's see it!
0 x

User avatar
Noman
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 714
Joined: Mon Jan 31, 2005 7:21 pm
Location: Israel
Contact:

Post by Noman » Wed Jan 09, 2008 1:57 pm

I finished an article on the wiki explaining the design of what I'm about to do. It can be found here :

http://www.ogre3d.org/wiki/index.php/Co ... _Framework

The first version will probably be ready and published in about a week (a bit less hopefully). It will be the same license as Ogre.

Once again, I'd like to get any feedback, so hopefully the solution will fit the community's needs as well.

PS : Wiki question : I put my code in the wiki in <pre> tags. In wikipedia's wiki, there is a <source> tag instead that lets you also specify the language used, and the engine parses it. Much nicer to read. ( http://en.wikipedia.org/wiki/C%2B%2B for an example). Why doesn't ogre's wiki have this? Is it a matter of configuration or upgrading the engine's version (or switching to a different one? They look the same)
0 x

User avatar
Assaf Raman
OGRE Team Member
OGRE Team Member
Posts: 3092
Joined: Tue Apr 11, 2006 3:58 pm
Location: TLV, Israel

Post by Assaf Raman » Wed Jan 09, 2008 2:05 pm

Nice diagrams. :D
0 x
Watch out for my OGRE related tweets here.

User avatar
Noman
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 714
Joined: Mon Jan 31, 2005 7:21 pm
Location: Israel
Contact:

Post by Noman » Wed Jan 09, 2008 5:10 pm

I'm already in the middle of the implementation, and I want to do a coding trick that some people might not like, so I'm writing about it here first.

Similar to ogre's plugin interface, a dll has to implement a function that returns an instance of the CompositorLogic.

It looks like this :

Code: Select all

extern "C" _CompositorPluginExport CompositorLogic* getCompositorLogic()
{ 
		 return new HDRLogic(); 
}
Instead of having each plugin write this code again, I want to declare a macro like this :

Code: Select all

#define DECLARE_COMPOSITOR_PLUGIN(CompositorLogicClassName) \
	extern "C" _CompositorPluginExport CompositorLogic* getCompositorLogic() \
		{ return new CompositorLogicClassName(); }
Which will make each plugin need to write this line :

Code: Select all

DECLARE_COMPOSITOR_PLUGIN(HDRLogic)
Do you guys have objections to this idea? (I also think that this system can be used with ogre's plugin to save a bit of code replication in the OgreD3D9EngineDll.cpp, OgreOctreeSceneManagerDll.cpp etc files)
0 x

User avatar
Praetor
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 3335
Joined: Tue Jun 21, 2005 8:26 pm
Location: Rochester, New York, US
Contact:

Post by Praetor » Wed Jan 09, 2008 6:03 pm

I prefer not to use macros when I don't have to. So, if I didn't want to use your macro I just wouldn't. If you wanted to use it there shouldn't be a problem.
0 x

User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19261
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
Contact:

Post by sinbad » Wed Jan 09, 2008 7:44 pm

Agreed, macro use should be minimised, we tend to only use them when really necessary (because of precompiler build options, or where we need other precompiler information).

At the very least any macro that appears in an Ogre header must be prefixed by 'OGRE_', but for something like this I don't think it's really justified.

For reference: OGRE Coding Standards
0 x

User avatar
Noman
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 714
Joined: Mon Jan 31, 2005 7:21 pm
Location: Israel
Contact:

Post by Noman » Thu Jan 10, 2008 5:17 pm

Ok, then the macro will stay out of the distribution.

I'm almost done with the implementation, mostly ironing out small issues and cleaning up the code for distribution left.

Here is a preview :

Image

Note that everything is completely dynamic. The compositor demo doesn't have any knowledge about the installed compositors (not even Gaussian Blur, HDR or Heat Vision), and what parameters they supported. What you see is a small wrapper CompositorLogic that doesn't do anything except expose NumTiles via a parameter.

I expect to finish cleaning everything up and releasing a functional first version early next week.
0 x

User avatar
Noman
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 714
Joined: Mon Jan 31, 2005 7:21 pm
Location: Israel
Contact:

Post by Noman » Sun Jan 13, 2008 12:51 pm

A first version is ready!

It can be found at
http://nomanogre.googlepages.com/CompositorLogic.zip

The zip currently needs to be unpacked in the ogrenew folder (ogre source release). It opens a new solution called CompositorLogic_vc8.sln. I tried downloading a fresh source+dependency distribution, building the main solution and then the CompositorLogic solution and it worked cleanly.

It should be possible to get this to work on the SDK release (currently packaged for the source release), but you'll have to modify the .vcproj scripts.
(None-VC8 users, you'll have to do the scriptology yourselves to use this, for now)

If you encounter any problems building it, please post problems here and I'll help.

As always, I'd like to get feedback from you guys :)

Have fun!
0 x

User avatar
Praetor
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 3335
Joined: Tue Jun 21, 2005 8:26 pm
Location: Rochester, New York, US
Contact:

Post by Praetor » Sun Jan 13, 2008 6:38 pm

Awesome, I'll take a look. Hopefully soon.
0 x

Post Reply