Hi All,
I've been thinking about the various classes and relationships that
we'll need to support both the front end API for shader composition,
and the backend implementation of it, and my current thoughts are:
1) The ShaderSet I've been discussing over my last few post should be renamed,
my current preferred name is ShaderComponent.
2) Rather than ShaderComponent (was ShaderSet) have the details of how to
inject code into the shader main for each of the vertex, geometry
and fragment
mains, I now feel it would be more managable to move injection support into
osg::Shader.
This would mean that ShaderComponent would then just have a list of
one or more
osg::Shader. These Shaders would then be grouped into ones that affect the
vertex, geometry and fragment programs.
The osg::Shader class would have some new API for setting up the inject code,
this could be empty/inactive in a default constructed osg::Shader,
so wouldn't
affect how we use osg::Shader/osg::Program right now. It's only when shader
composition comes into play will these extra fields be queried.
3) StateAttribute would "have a" ShaderComponent that implements the
shader functionality, this ShaderComponent would typically be shared between
the same type of StateAttribute. The StateAttribute attribute
would also provide
osg::Uniform that pass in the values to the associated ShaderComponent, these
osg::Unfirom will be applied by the existing
StateAttribute::apply(..) method.
This is approach I've been discussing before (save for the ShaderSet rename.)
4) osg::State will maintain a current list of enabled ShaderComponent's, this
list of pointers will form a key to search for the appropriate osg::Program
to apply to achieve that functionality. The way that the osg::Program will
be wrapped up and cached is within a ShaderAssembly. osg::State
would internally manage the creation of ShaderAssembly, and cache of these
and apply the osg::Program they contain.
Lazy state updating in osg::State will seek to minimize the times that
the state is changed between ShaderAssembly. When the set/list of enabled
ShaderComponent's changes a the appropriate ShaderAssumbly for this set
of ShaderComponent is then lookup in the ShaderAssembly cache. If
non apporpriate ShaderAssembly is found then the ShaderComposer is
invoked to create a new ShaderAssembly which is then cached and made
current.
5) A ShaderAssembly is an internal implementation class so not something
a user would normally worry about, only the ShaderComposer or
subclasses from it would need to know about it.
A ShaderAssembly has the final osg::Program that is applied to OpenGL,
this osg::Program is composed on the osg::Shader's provided by the
osg::ShaderComponent, and also an automatically created osg::Shader
main for each of the vertex, geometry and fragment parts osg::Program.
The automatically generated shader mains are wrapped up in a ShaderMain
class that has a list of osg::Shader that contribute to it, these
osg::Shader
are pulled in from the ShaderComponent's that are associated with the
ShaderAssembly. The individual osg::Shader that assigned to a ShaderMain
provide the code injection details that enable the ShaderComposer to create
the final main() code that gets placed in the ShaderMain's automatically
generated osg::Shader.
The ShaderAssembly contains a ShaderMain for each of the vertex, geometry
and fragment programs. Pulling all the Shaders, both provided by the
ShaderComponent and the automatically generated ones in the three
ShaderMain to create the final osg::Program.
It will be possible to share ShaderMain between multiple ShaderAssemly, and
this will be desirable as often we will just enable/disable a
mode that affects
only the vertex shaders parts, or just the fragment shader parts,
so if we are
able to share then we only need create a new ShaderMain for the part that
changes, the rest can be reused from a cache of ShaderMain (that will be
provided by osg::ShaderComposer).
Like ShaderAssembly the ShaderMain is an implementation detail that most
end users need not use directly or worry about. It's only osg::State and
osg::ShaderComposer (or subclasses from it) that will directly
deal with them.
6) ShaderComposer will manage a cache of ShaderAssembly, and access to
this cache and the automatic creation of new ShaderAssembly when a
new combination of enabled ShaderComponent is requested. When a
a new ShaderAssembly is created the ShaderComposer querries the
ShaderComponent to work out what ShaderMain it needs to create, and where
possible to pull these in from a cache of ShaderMain.
osg::State "has a" ShaderComposer, and will defer most of the shader
composition functionality to it. Users will be able to subclass from
ShaderComposer to provide their own custom schemes for creating and
managing the required osg::Program. Such as subclass could even
roll their own ShaderMain/ShaderAssembly classes as we might well
be able to hide these implementation details entirely within the
ShaderComposer
base class.
7) In terms of ease of design and management it would be easiest for use to
by able to assume that ShaderComponent and the osg::Shader they contain
are all constant. If a user want to change the shaders they they create a
new osg::Shader and a new ShaderComponent for them rather than reuse
an existing one and tweak it.
We could possible enforce that const behaviour by having a toggle in
ShaderComponent and osg::Shader that once switched on will make these
classes ignore changes to them, or at least warn of the change might
break the ShaderAssembly cache.
The alternative being able to make the const assumption would be for us
to implement a dirty count in Shader and ShaderComponent such that
any associated ShaderAssembly would have to be recreated. Perhaps
this might not be too complicated to implement, but wouldn't be without
a CPU overhead in checking for shader components.
8) ShaderComponent would provide guidance on what traditional fixed
function gglEnable/glDisable GLenum's that take over the role of,
these GLenums we set right now via osg::StateSet::setMode(..). The
guidance provided by ShaderComponent will enable osg::State to know
which modes to redirect to enabling the associated ShaderComponent
and which should be ingored or still passed on to OpenGL via
glEnable/glDisable.
OK. That's my current thoughts... I'm getting reasonably comfortable
with the different parts, not everything is settled but I'm probably
far enough along now to start doing
some coding up which hopefully get a chance to start tackling over the
next couple of days. It's school holidays right now though so I'm
looking after my brood for most of today, so don't expect too many
check-in's right away.
Let me know your thoughts/suggestions.
Robert.
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org