Example #1
0
void
VirtualProgram::apply( osg::State& state ) const
{
    if (_shaderMap.empty() && !_inheritSet)
    {
        // If there's no data in the VP, and never has been, unload any existing program.
        // NOTE: OSG's State processor creates a "global default attribute" for each type.
        // Sine we have no way of knowing whether the user created the VP or OSG created it
        // as the default fallback, we use the "_inheritSet" flag to differeniate. This
        // prevents any shader leakage from a VP-enabled node.
        const unsigned int contextID = state.getContextID();
        const osg::GL2Extensions* extensions = osg::GL2Extensions::Get(contextID,true);
        if( ! extensions->isGlslSupported() ) return;

        extensions->glUseProgram( 0 );
        state.setLastAppliedProgramObject(0);
        return;
    }

    // first, find and collect all the VirtualProgram attributes:
    ShaderMap         accumShaderMap;
    AttribBindingList accumAttribBindings;
    AttribAliasMap    accumAttribAliases;
    
    if ( _inherit )
    {
        const StateHack::AttributeVec* av = StateHack::GetAttributeVec( state, this );
        if ( av && av->size() > 0 )
        {
            // find the deepest VP that doesn't inherit:
            unsigned start = 0;
            for( start = (int)av->size()-1; start > 0; --start )
            {
                const VirtualProgram* vp = dynamic_cast<const VirtualProgram*>( (*av)[start].first );
                if ( vp && (vp->_mask & _mask) && vp->_inherit == false )
                    break;
            }
            
            // collect shaders from there to here:
            for( unsigned i=start; i<av->size(); ++i )
            {
                const VirtualProgram* vp = dynamic_cast<const VirtualProgram*>( (*av)[i].first );
                if ( vp && (vp->_mask && _mask) )
                {
                    ShaderMap vpShaderMap;
                    vp->getShaderMap( vpShaderMap );

                    for( ShaderMap::const_iterator i = vpShaderMap.begin(); i != vpShaderMap.end(); ++i )
                    {
                        addToAccumulatedMap( accumShaderMap, i->first, i->second );
                    }

                    const AttribBindingList& abl = vp->getAttribBindingList();
                    accumAttribBindings.insert( abl.begin(), abl.end() );

#ifdef USE_ATTRIB_ALIASES
                    const AttribAliasMap& aliases = vp->getAttribAliases();
                    accumAttribAliases.insert( aliases.begin(), aliases.end() );
#endif
                }
            }
        }
    }

    // next add the local shader components to the map, respecting the override values:
    {
        Threading::ScopedReadLock readonly(_dataModelMutex);

        for( ShaderMap::const_iterator i = _shaderMap.begin(); i != _shaderMap.end(); ++i )
        {
            addToAccumulatedMap( accumShaderMap, i->first, i->second );
        }

        const AttribBindingList& abl = this->getAttribBindingList();
        accumAttribBindings.insert( abl.begin(), abl.end() );

#ifdef USE_ATTRIB_ALIASES
        const AttribAliasMap& aliases = this->getAttribAliases();
        accumAttribAliases.insert( aliases.begin(), aliases.end() );
#endif
    }


    if ( true ) //even with nothing in the map, we still want mains! -gw  //accumShaderMap.size() )
    {
        // next, assemble a list of the shaders in the map so we can use it as our
        // program cache key.
        // (Note: at present, the "cache key" does not include any information on the vertex
        // attribute bindings. Technically it should, but in practice this might not be an
        // issue; it is unlikely one would have two identical shader programs with different
        // bindings.)
        ShaderVector vec;
        vec.reserve( accumShaderMap.size() );
        for( ShaderMap::iterator i = accumShaderMap.begin(); i != accumShaderMap.end(); ++i )
        {
            ShaderEntry& entry = i->second;
            vec.push_back( entry.first.get() );
        }
        
        // see if there's already a program associated with this list:
        osg::ref_ptr<osg::Program> program;
        
        // look up the program:
        {
            Threading::ScopedReadLock shared( _programCacheMutex );
            
            ProgramMap::const_iterator p = _programCache.find( vec );
            if ( p != _programCache.end() )
            {
                program = p->second.get();
            }
        }
        
        // if not found, lock and build it:
        if ( !program.valid() )
        {
            // build a new set of accumulated functions, to support the creation of main()
            ShaderComp::FunctionLocationMap accumFunctions;
            accumulateFunctions( state, accumFunctions );

            // now double-check the program cache, and failing that, build the
            // new shader Program.
            {
                Threading::ScopedWriteLock exclusive( _programCacheMutex );
                
                // double-check: look again ito negate race conditions
                ProgramMap::const_iterator p = _programCache.find( vec );
                if ( p != _programCache.end() )
                {
                    program = p->second.get();
                }
                else
                {
                    ShaderVector keyVector;

                    //OE_NOTICE << LC << "Building new Program for VP " << getName() << std::endl;

                    program = buildProgram(
                        getName(),
                        state,
                        accumFunctions,
                        accumShaderMap, 
                        accumAttribBindings, 
                        accumAttribAliases, 
                        _template.get(),
                        keyVector);

                    // finally, put own new program in the cache.
                    _programCache[ keyVector ] = program;
                }
            }
        }
        
        // finally, apply the program attribute.
        if ( program.valid() )
        {
            program->apply( state );
        }
    }
}
void
VirtualProgram::apply( osg::State& state ) const
{
    if ( _shaderMap.empty() )
    {
        // if there's no data in the VP, unload any existing program.
        const unsigned int contextID = state.getContextID();
        const osg::GL2Extensions* extensions = osg::GL2Extensions::Get(contextID,true);
        if( ! extensions->isGlslSupported() ) return;

        extensions->glUseProgram( 0 );
        state.setLastAppliedProgramObject(0);
        return;
    }

    // first, find and collect all the VirtualProgram attributes:
    ShaderMap         accumShaderMap;
    AttribBindingList accumAttribBindings;
    
    if ( _inherit )
    {
        const StateHack::AttributeVec* av = StateHack::GetAttributeVec( state, this );
        if ( av && av->size() > 0 )
        {
            // find the deepest VP that doesn't inherit:
            unsigned start = 0;
            for( start = (int)av->size()-1; start > 0; --start )
            {
                const VirtualProgram* vp = dynamic_cast<const VirtualProgram*>( (*av)[start].first );
                if ( vp && (vp->_mask & _mask) && vp->_inherit == false )
                    break;
            }
            
            // collect shaders from there to here:
            for( unsigned i=start; i<av->size(); ++i )
            {
                const VirtualProgram* vp = dynamic_cast<const VirtualProgram*>( (*av)[i].first );
                if ( vp && (vp->_mask && _mask) )
                {
                    for( ShaderMap::const_iterator i = vp->_shaderMap.begin(); i != vp->_shaderMap.end(); ++i )
                    {
                        addToAccumulatedMap( accumShaderMap, i->first, i->second );
                    }

                    const AttribBindingList& abl = vp->getAttribBindingList();
                    accumAttribBindings.insert( abl.begin(), abl.end() );
                }
            }
        }
    }
    
    // next add the local shader components to the map, respecting the override values:
    for( ShaderMap::const_iterator i = _shaderMap.begin(); i != _shaderMap.end(); ++i )
    {
        addToAccumulatedMap( accumShaderMap, i->first, i->second );
    }
    const AttribBindingList& abl = this->getAttribBindingList();
    accumAttribBindings.insert( abl.begin(), abl.end() );
    
    
    if ( accumShaderMap.size() )
    {
        // next, assemble a list of the shaders in the map so we can use it as our
        // program cache key.
        // (Note: at present, the "cache key" does not include any information on the vertex
        // attribute bindings. Technically it should, but in practice this might not be an
        // issue; it is unlikely one would have two identical shader programs with different
        // bindings.)
        ShaderVector vec;
        vec.reserve( accumShaderMap.size() );
        for( ShaderMap::iterator i = accumShaderMap.begin(); i != accumShaderMap.end(); ++i )
        {
            ShaderEntry& entry = i->second;
            vec.push_back( entry.first.get() );
        }
        
        // see if there's already a program associated with this list:
        osg::Program* program = 0L;
        
        // look up the program:
        {
            Threading::ScopedReadLock shared( _programCacheMutex );
            
            ProgramMap::const_iterator p = _programCache.find( vec );
            if ( p != _programCache.end() )
            {
                program = p->second.get();
            }
        }
        
        // if not found, lock and build it:
        if ( !program )
        {
            Threading::ScopedWriteLock exclusive( _programCacheMutex );
            
            // look again in case of contention:
            ProgramMap::const_iterator p = _programCache.find( vec );
            if ( p != _programCache.end() )
            {
                program = p->second.get();
            }
            else
            {
                VirtualProgram* nc = const_cast<VirtualProgram*>(this);
                program = nc->buildProgram( state, accumShaderMap, accumAttribBindings );
            }
        }
        
        // finally, apply the program attribute.
        program->apply( state );
    }
}