root/OpenSceneGraph/trunk/src/osg/Program.cpp @ 9989

Revision 9989, 18.4 kB (checked in by robert, 6 years ago)

From Simon Carmody, "Patches for compare and copy of Programs and related state
-Added copying of shaders and attributes in osg::Program copy constructor.
-Changed StateSet::compare function to compare Uniforms and their
override values. Previously it compared a RefUniformPair?."

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 * Copyright (C) 2003-2005 3Dlabs Inc. Ltd.
3 * Copyright (C) 2004-2005 Nathan Cournia
4 * Copyright (C) 2008 Zebra Imaging
5 *
6 * This application is open source and may be redistributed and/or modified   
7 * freely and without restriction, both in commericial and non commericial
8 * applications, as long as this copyright notice is maintained.
9 *
10 * This application is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 *
14*/
15
16/* file:        src/osg/Program.cpp
17 * author:      Mike Weiblen 2008-01-19
18*/
19
20#include <list>
21
22#include <osg/Notify>
23#include <osg/State>
24#include <osg/Timer>
25#include <osg/buffered_value>
26#include <osg/ref_ptr>
27#include <osg/Program>
28#include <osg/Shader>
29#include <osg/GL2Extensions>
30
31#include <OpenThreads/ScopedLock>
32#include <OpenThreads/Mutex>
33
34using namespace osg;
35
36///////////////////////////////////////////////////////////////////////////
37// static cache of glPrograms flagged for deletion, which will actually
38// be deleted in the correct GL context.
39
40typedef std::list<GLuint> GlProgramHandleList;
41typedef osg::buffered_object<GlProgramHandleList> DeletedGlProgramCache;
42
43static OpenThreads::Mutex    s_mutex_deletedGlProgramCache;
44static DeletedGlProgramCache s_deletedGlProgramCache;
45
46void Program::deleteGlProgram(unsigned int contextID, GLuint program)
47{
48    if( program )
49    {
50        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedGlProgramCache);
51
52        // add glProgram to the cache for the appropriate context.
53        s_deletedGlProgramCache[contextID].push_back(program);
54    }
55}
56
57void Program::flushDeletedGlPrograms(unsigned int contextID,double /*currentTime*/, double& availableTime)
58{
59    // if no time available don't try to flush objects.
60    if (availableTime<=0.0) return;
61
62    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedGlProgramCache);
63    const GL2Extensions* extensions = GL2Extensions::Get(contextID,true);
64    if( ! extensions->isGlslSupported() ) return;
65
66    const osg::Timer& timer = *osg::Timer::instance();
67    osg::Timer_t start_tick = timer.tick();
68    double elapsedTime = 0.0;
69
70    {
71
72        GlProgramHandleList& pList = s_deletedGlProgramCache[contextID];
73        for(GlProgramHandleList::iterator titr=pList.begin();
74            titr!=pList.end() && elapsedTime<availableTime;
75            )
76        {
77            extensions->glDeleteProgram( *titr );
78            titr = pList.erase( titr );
79            elapsedTime = timer.delta_s(start_tick,timer.tick());
80        }
81    }
82
83    availableTime -= elapsedTime;
84}
85
86void Program::discardDeletedGlPrograms(unsigned int contextID)
87{
88    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedGlProgramCache);
89    GlProgramHandleList& pList = s_deletedGlProgramCache[contextID];
90    pList.clear();
91}
92
93
94///////////////////////////////////////////////////////////////////////////
95// osg::Program
96///////////////////////////////////////////////////////////////////////////
97
98Program::Program() :
99    _geometryVerticesOut(1), _geometryInputType(GL_TRIANGLES),
100    _geometryOutputType(GL_TRIANGLE_STRIP)
101{
102}
103
104
105Program::Program(const Program& rhs, const osg::CopyOp& copyop):
106    osg::StateAttribute(rhs, copyop)
107{
108    for( unsigned int shaderIndex=0; shaderIndex < rhs.getNumShaders(); ++shaderIndex )
109    {
110        addShader( new osg::Shader( *rhs.getShader( shaderIndex ), copyop ) );
111    }
112
113    const osg::Program::AttribBindingList &abl = rhs.getAttribBindingList();
114    for( osg::Program::AttribBindingList::const_iterator attribute = abl.begin(); attribute != abl.end(); ++attribute )
115    {
116        addBindAttribLocation( attribute->first, attribute->second );
117    }
118
119    const osg::Program::FragDataBindingList &fdl = rhs.getFragDataBindingList();
120    for( osg::Program::FragDataBindingList::const_iterator fragdata = fdl.begin(); fragdata != fdl.end(); ++fragdata )
121    {
122        addBindFragDataLocation( fragdata->first, fragdata->second );
123    }
124
125    _geometryVerticesOut = rhs._geometryVerticesOut;
126    _geometryInputType = rhs._geometryInputType;
127    _geometryOutputType = rhs._geometryOutputType;
128}
129
130
131Program::~Program()
132{
133    // inform any attached Shaders that we're going away
134    for( unsigned int i=0; i < _shaderList.size(); ++i )
135    {
136        _shaderList[i]->removeProgramRef( this );
137    }
138}
139
140
141int Program::compare(const osg::StateAttribute& sa) const
142{
143    // check the types are equal and then create the rhs variable
144    // used by the COMPARE_StateAttribute_Parameter macro's below.
145    COMPARE_StateAttribute_Types(Program,sa)
146   
147    if( _shaderList.size() < rhs._shaderList.size() ) return -1;
148    if( rhs._shaderList.size() < _shaderList.size() ) return 1;
149
150    if( getName() < rhs.getName() ) return -1;
151    if( rhs.getName() < getName() ) return 1;
152
153    if( _geometryVerticesOut < rhs._geometryVerticesOut ) return -1;
154    if( rhs._geometryVerticesOut < _geometryVerticesOut ) return 1;
155
156    if( _geometryInputType < rhs._geometryInputType ) return -1;
157    if( rhs._geometryInputType < _geometryInputType ) return 1;
158
159    if( _geometryOutputType < rhs._geometryOutputType ) return -1;
160    if( rhs._geometryOutputType < _geometryOutputType ) return 1;
161
162    ShaderList::const_iterator litr=_shaderList.begin();
163    ShaderList::const_iterator ritr=rhs._shaderList.begin();
164    for(;
165        litr!=_shaderList.end();
166        ++litr,++ritr)
167    {
168        int result = (*litr)->compare(*(*ritr));
169        if (result!=0) return result;
170    }
171
172    return 0; // passed all the above comparison macro's, must be equal.
173}
174
175
176void Program::compileGLObjects( osg::State& state ) const
177{
178    if( isFixedFunction() ) return;
179
180    const unsigned int contextID = state.getContextID();
181
182    for( unsigned int i=0; i < _shaderList.size(); ++i )
183    {
184        _shaderList[i]->compileShader( contextID );
185    }
186
187    getPCP( contextID )->linkProgram();
188}
189
190void Program::setThreadSafeRefUnref(bool threadSafe)
191{
192    StateAttribute::setThreadSafeRefUnref(threadSafe);
193
194    for( unsigned int i=0; i < _shaderList.size(); ++i )
195    {
196        if (_shaderList[i].valid()) _shaderList[i]->setThreadSafeRefUnref(threadSafe);
197    }
198}
199
200void Program::dirtyProgram()
201{
202    // mark our PCPs as needing relink
203    for( unsigned int cxt=0; cxt < _pcpList.size(); ++cxt )
204    {
205        if( _pcpList[cxt].valid() ) _pcpList[cxt]->requestLink();
206    }
207}
208
209
210void Program::resizeGLObjectBuffers(unsigned int maxSize)
211{
212    for( unsigned int i=0; i < _shaderList.size(); ++i )
213    {
214        if (_shaderList[i].valid()) _shaderList[i]->resizeGLObjectBuffers(maxSize);
215    }
216
217    _pcpList.resize(maxSize);
218}
219
220void Program::releaseGLObjects(osg::State* state) const
221{
222    for( unsigned int i=0; i < _shaderList.size(); ++i )
223    {
224        if (_shaderList[i].valid()) _shaderList[i]->releaseGLObjects(state);
225    }
226
227    if (!state) _pcpList.setAllElementsTo(0);
228    else
229    {
230        unsigned int contextID = state->getContextID();
231        _pcpList[contextID] = 0;
232    }   
233}
234
235
236bool Program::addShader( Shader* shader )
237{
238    if( !shader ) return false;
239
240    // Shader can only be added once to a Program
241    for( unsigned int i=0; i < _shaderList.size(); ++i )
242    {
243        if( shader == _shaderList[i].get() ) return false;
244    }
245
246    // Add shader to PCPs
247    for( unsigned int cxt=0; cxt < _pcpList.size(); ++cxt )
248    {
249        if( _pcpList[cxt].valid() ) _pcpList[cxt]->addShaderToAttach( shader );
250    }
251
252    shader->addProgramRef( this );
253    _shaderList.push_back( shader );
254    dirtyProgram();
255    return true;
256}
257
258
259bool Program::removeShader( Shader* shader )
260{
261    if( !shader ) return false;
262
263    // Shader must exist to be removed.
264    for( ShaderList::iterator itr = _shaderList.begin();
265         itr != _shaderList.end();
266         ++itr)
267    {
268        if( shader == itr->get() )
269        {
270            // Remove shader from PCPs
271            for( unsigned int cxt=0; cxt < _pcpList.size(); ++cxt )
272            {
273                if( _pcpList[cxt].valid() ) _pcpList[cxt]->addShaderToDetach( shader );
274            }
275
276            shader->removeProgramRef( this );
277            _shaderList.erase(itr);
278
279            dirtyProgram();
280            return true;
281        }
282    }
283
284    return false;
285}
286
287
288void Program::setParameter( GLenum pname, GLint value )
289{
290    switch( pname )
291    {
292        case GL_GEOMETRY_VERTICES_OUT_EXT:
293            _geometryVerticesOut = value;
294            dirtyProgram();
295            break;
296        case GL_GEOMETRY_INPUT_TYPE_EXT:
297            _geometryInputType = value;
298            dirtyProgram();    // needed?
299            break;
300        case GL_GEOMETRY_OUTPUT_TYPE_EXT:
301            _geometryOutputType = value;
302            dirtyProgram();    // needed?
303            break;
304        default:
305            osg::notify(osg::WARN) << "setParameter invalid param " << pname << std::endl;
306            break;
307    }
308}
309
310GLint Program::getParameter( GLenum pname ) const
311{
312    switch( pname )
313    {
314        case GL_GEOMETRY_VERTICES_OUT_EXT: return _geometryVerticesOut;
315        case GL_GEOMETRY_INPUT_TYPE_EXT:   return _geometryInputType;
316        case GL_GEOMETRY_OUTPUT_TYPE_EXTreturn _geometryOutputType;
317    }
318    osg::notify(osg::WARN) << "getParameter invalid param " << pname << std::endl;
319    return 0;
320}
321
322
323void Program::addBindAttribLocation( const std::string& name, GLuint index )
324{
325    _attribBindingList[name] = index;
326    dirtyProgram();
327}
328
329void Program::removeBindAttribLocation( const std::string& name )
330{
331    _attribBindingList.erase(name);
332    dirtyProgram();
333}
334
335void Program::addBindFragDataLocation( const std::string& name, GLuint index )
336{
337    _fragDataBindingList[name] = index;
338    dirtyProgram();
339}
340
341void Program::removeBindFragDataLocation( const std::string& name )
342{
343    _fragDataBindingList.erase(name);
344    dirtyProgram();
345}
346
347void Program::apply( osg::State& state ) const
348{
349    const unsigned int contextID = state.getContextID();
350    const GL2Extensions* extensions = GL2Extensions::Get(contextID,true);
351    if( ! extensions->isGlslSupported() ) return;
352
353    if( isFixedFunction() )
354    {
355        extensions->glUseProgram( 0 );
356        state.setLastAppliedProgramObject(0);
357        return;
358    }
359
360    PerContextProgram* pcp = getPCP( contextID );
361    if( pcp->needsLink() ) compileGLObjects( state );
362    if( pcp->isLinked() )
363    {
364        // for shader debugging: to minimize performance impact,
365        // optionally validate based on notify level.
366        // TODO: enable this using notify level, or perhaps its own getenv()?
367        if( osg::isNotifyEnabled(osg::INFO) )
368            pcp->validateProgram();
369
370        pcp->useProgram();
371        state.setLastAppliedProgramObject(pcp);
372    }
373    else
374    {
375        // program not usable, fallback to fixed function.
376        extensions->glUseProgram( 0 );
377        state.setLastAppliedProgramObject(0);
378    }
379}
380
381
382Program::PerContextProgram* Program::getPCP(unsigned int contextID) const
383{
384    if( ! _pcpList[contextID].valid() )
385    {
386        _pcpList[contextID] = new PerContextProgram( this, contextID );
387
388        // attach all PCSs to this new PCP
389        for( unsigned int i=0; i < _shaderList.size(); ++i )
390        {
391            _pcpList[contextID]->addShaderToAttach( _shaderList[i].get() );
392        }
393    }
394
395    return _pcpList[contextID].get();
396}
397
398
399bool Program::isFixedFunction() const
400{
401    // A Program object having no attached Shaders is a special case:
402    // it indicates that programmable shading is to be disabled,
403    // and thus use GL 1.x "fixed functionality" rendering.
404    return _shaderList.empty();
405}
406
407
408bool Program::getGlProgramInfoLog(unsigned int contextID, std::string& log) const
409{
410    return getPCP( contextID )->getInfoLog( log );
411}
412
413const Program::ActiveVarInfoMap& Program::getActiveUniforms(unsigned int contextID) const
414{
415    return getPCP( contextID )->getActiveUniforms();
416}
417
418const Program::ActiveVarInfoMap& Program::getActiveAttribs(unsigned int contextID) const
419{
420    return getPCP( contextID )->getActiveAttribs();
421}
422
423///////////////////////////////////////////////////////////////////////////
424// osg::Program::PerContextProgram
425// PCP is an OSG abstraction of the per-context glProgram
426///////////////////////////////////////////////////////////////////////////
427
428Program::PerContextProgram::PerContextProgram(const Program* program, unsigned int contextID ) :
429        osg::Referenced(),
430        _contextID( contextID )
431{
432    _program = program;
433    _extensions = GL2Extensions::Get( _contextID, true );
434    _glProgramHandle = _extensions->glCreateProgram();
435    requestLink();
436}
437
438Program::PerContextProgram::~PerContextProgram()
439{
440    Program::deleteGlProgram( _contextID, _glProgramHandle );
441}
442
443
444void Program::PerContextProgram::requestLink()
445{
446    _needsLink = true;
447    _isLinked = false;
448}
449
450
451void Program::PerContextProgram::linkProgram()
452{
453    if( ! _needsLink ) return;
454    _needsLink = false;
455
456    osg::notify(osg::INFO)
457        << "Linking osg::Program \"" << _program->getName() << "\""
458        << " id=" << _glProgramHandle
459        << " contextID=" << _contextID
460        <<  std::endl;
461
462    if (_extensions->isGeometryShader4Supported())
463    {
464        _extensions->glProgramParameteri( _glProgramHandle, GL_GEOMETRY_VERTICES_OUT_EXT, _program->_geometryVerticesOut );
465        _extensions->glProgramParameteri( _glProgramHandle, GL_GEOMETRY_INPUT_TYPE_EXT, _program->_geometryInputType );
466        _extensions->glProgramParameteri( _glProgramHandle, GL_GEOMETRY_OUTPUT_TYPE_EXT, _program->_geometryOutputType );
467    }
468   
469    // Detach removed shaders
470    for( unsigned int i=0; i < _shadersToDetach.size(); ++i )
471    {
472        _shadersToDetach[i]->detachShader( _contextID, _glProgramHandle );
473    }
474    _shadersToDetach.clear();
475   
476    // Attach new shaders
477    for( unsigned int i=0; i < _shadersToAttach.size(); ++i )
478    {
479        _shadersToAttach[i]->attachShader( _contextID, _glProgramHandle );
480    }
481    _shadersToAttach.clear();
482
483    _uniformInfoMap.clear();
484    _attribInfoMap.clear();
485    _lastAppliedUniformList.clear();
486
487    // set any explicit vertex attribute bindings
488    const AttribBindingList& bindlist = _program->getAttribBindingList();
489    for( AttribBindingList::const_iterator itr = bindlist.begin();
490        itr != bindlist.end(); ++itr )
491    {
492        _extensions->glBindAttribLocation( _glProgramHandle, itr->second, itr->first.c_str() );
493    }
494
495    // set any explicit frag data bindings
496    const FragDataBindingList& fdbindlist = _program->getFragDataBindingList();
497    for( FragDataBindingList::const_iterator itr = fdbindlist.begin();
498        itr != fdbindlist.end(); ++itr )
499    {
500        _extensions->glBindFragDataLocation( _glProgramHandle, itr->second, itr->first.c_str() );
501    }
502
503    // link the glProgram
504    GLint linked = GL_FALSE;
505    _extensions->glLinkProgram( _glProgramHandle );
506    _extensions->glGetProgramiv( _glProgramHandle, GL_LINK_STATUS, &linked );
507    _isLinked = (linked == GL_TRUE);
508    if( ! _isLinked )
509    {
510        osg::notify(osg::WARN) << "glLinkProgram \""<< _program->getName() << "\" FAILED" << std::endl;
511
512        std::string infoLog;
513        if( getInfoLog(infoLog) )
514        {
515            osg::notify(osg::WARN) << "Program \""<< _program->getName() << "\" " 
516                                      "infolog:\n" << infoLog << std::endl;
517        }
518       
519        return;
520    }
521    else
522    {
523        std::string infoLog;
524        if( getInfoLog(infoLog) )
525        {
526            osg::notify(osg::INFO) << "Program \""<< _program->getName() << "\" "<<
527                                      "link succeded, infolog:\n" << infoLog << std::endl;
528        }
529    }
530
531    // build _uniformInfoMap
532    GLint numUniforms = 0;
533    GLsizei maxLen = 0;
534    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_UNIFORMS, &numUniforms );
535    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen );
536    if( (numUniforms > 0) && (maxLen > 1) )
537    {
538        GLint size = 0;
539        GLenum type = 0;
540        GLchar* name = new GLchar[maxLen];
541
542        for( GLint i = 0; i < numUniforms; ++i )
543        {
544            _extensions->glGetActiveUniform( _glProgramHandle,
545                    i, maxLen, 0, &size, &type, name );
546
547            GLint loc = _extensions->glGetUniformLocation( _glProgramHandle, name );
548           
549            if( loc != -1 )
550            {
551                _uniformInfoMap[name] = ActiveVarInfo(loc,type,size);
552
553                osg::notify(osg::INFO)
554                    << "\tUniform \"" << name << "\""
555                    << " loc="<< loc
556                    << " size="<< size
557                    << " type=" << Uniform::getTypename((Uniform::Type)type)
558                    << std::endl;
559            }
560        }
561        delete [] name;
562    }
563
564    // build _attribInfoMap
565    GLint numAttrib = 0;
566    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_ATTRIBUTES, &numAttrib );
567    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLen );
568    if( (numAttrib > 0) && (maxLen > 1) )
569    {
570        GLint size = 0;
571        GLenum type = 0;
572        GLchar* name = new GLchar[maxLen];
573
574        for( GLint i = 0; i < numAttrib; ++i )
575        {
576            _extensions->glGetActiveAttrib( _glProgramHandle,
577                    i, maxLen, 0, &size, &type, name );
578
579            GLint loc = _extensions->glGetAttribLocation( _glProgramHandle, name );
580           
581            if( loc != -1 )
582            {
583                _attribInfoMap[name] = ActiveVarInfo(loc,type,size);
584
585                osg::notify(osg::INFO)
586                    << "\tAttrib \"" << name << "\""
587                    << " loc=" << loc
588                    << " size=" << size
589                    << std::endl;
590            }
591        }
592        delete [] name;
593    }
594    osg::notify(osg::INFO) << std::endl;
595}
596
597bool Program::PerContextProgram::validateProgram()
598{
599    GLint validated = GL_FALSE;
600    _extensions->glValidateProgram( _glProgramHandle );
601    _extensions->glGetProgramiv( _glProgramHandle, GL_VALIDATE_STATUS, &validated );
602    if( validated == GL_TRUE)
603        return true;
604
605    osg::notify(osg::INFO)
606        << "glValidateProgram FAILED \"" << _program->getName() << "\""
607        << " id=" << _glProgramHandle
608        << " contextID=" << _contextID
609        <<  std::endl;
610
611    std::string infoLog;
612    if( getInfoLog(infoLog) )
613        osg::notify(osg::INFO) << "infolog:\n" << infoLog << std::endl;
614
615    osg::notify(osg::INFO) << std::endl;
616   
617    return false;
618}
619
620bool Program::PerContextProgram::getInfoLog( std::string& infoLog ) const
621{
622    return _extensions->getProgramInfoLog( _glProgramHandle, infoLog );
623}
624
625void Program::PerContextProgram::useProgram() const
626{
627    _extensions->glUseProgram( _glProgramHandle  );
628}
Note: See TracBrowser for help on using the browser.