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; // Build the active shader map up to this point: if ( _inherit ) { accumulateShaders(state, _mask, accumShaderMap, accumAttribBindings, accumAttribAliases); } // 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 ) { if ( i->second.accept(state) ) { 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 } // 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; if ( i->second.accept(state) ) { vec.push_back( entry._shader.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); // global sharing. s_programRepo.share(program); // finally, put own new program in the cache. _programCache[ keyVector ] = program; } } } // finally, apply the program attribute. if ( program.valid() ) { const unsigned int contextID = state.getContextID(); const osg::GL2Extensions* extensions = osg::GL2Extensions::Get(contextID,true); osg::Program::PerContextProgram* pcp = program->getPCP( contextID ); bool useProgram = state.getLastAppliedProgramObject() != pcp; #ifdef DEBUG_APPLY_COUNTS { // debugging static int s_framenum = 0; static Threading::Mutex s_mutex; static std::map< const VirtualProgram*, std::pair<int,int> > s_counts; Threading::ScopedMutexLock lock(s_mutex); int framenum = state.getFrameStamp()->getFrameNumber(); if ( framenum > s_framenum ) { OE_NOTICE << LC << "Applies in last frame: " << std::endl; for(std::map<const VirtualProgram*,std::pair<int,int> >::iterator i = s_counts.begin(); i != s_counts.end(); ++i) { std::pair<int,int>& counts = i->second; OE_NOTICE << LC << " " << i->first->getName() << " : " << counts.second << "/" << counts.first << std::endl; } s_framenum = framenum; s_counts.clear(); } s_counts[this].first++; if ( useProgram ) s_counts[this].second++; } #endif if ( useProgram ) { if( pcp->needsLink() ) program->compileGLObjects( state ); if( pcp->isLinked() ) { if( osg::isNotifyEnabled(osg::INFO) ) pcp->validateProgram(); pcp->useProgram(); state.setLastAppliedProgramObject( pcp ); } else { // program not usable, fallback to fixed function. extensions->glUseProgram( 0 ); state.setLastAppliedProgramObject(0); OE_WARN << LC << "Program link failure!" << std::endl; } } //program->apply( state ); #if 0 // test code for detecting race conditions for(int i=0; i<10000; ++i) { state.setLastAppliedProgramObject(0L); program->apply( state ); } #endif } }
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) ) { 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() ); const AttribAliasMap& aliases = vp->getAttribAliases(); accumAttribAliases.insert( aliases.begin(), aliases.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() ); const AttribAliasMap& aliases = this->getAttribAliases(); accumAttribAliases.insert( aliases.begin(), aliases.end() ); 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::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, accumAttribAliases); } } // finally, apply the program attribute. program->apply( state ); } }
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 ); #if 0 // test code for detecting race conditions for(int i=0; i<10000; ++i) { state.setLastAppliedProgramObject(0L); program->apply( state ); } #endif } } }