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

Revision 8296, 17.7 kB (checked in by robert, 6 years ago)

From Art Trevs, set the _geometryVerticesOut to default to 1 as a workaround
for OpenGL driver bug that incorrectly reports a warning when value is 0.

  • 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    osg::notify(osg::FATAL) << "how got here?" << std::endl;
109    _geometryVerticesOut = rhs._geometryVerticesOut;
110    _geometryInputType = rhs._geometryInputType;
111    _geometryOutputType = rhs._geometryOutputType;
112}
113
114
115Program::~Program()
116{
117    // inform any attached Shaders that we're going away
118    for( unsigned int i=0; i < _shaderList.size(); ++i )
119    {
120        _shaderList[i]->removeProgramRef( this );
121    }
122}
123
124
125int Program::compare(const osg::StateAttribute& sa) const
126{
127    // check the types are equal and then create the rhs variable
128    // used by the COMPARE_StateAttribute_Parameter macro's below.
129    COMPARE_StateAttribute_Types(Program,sa)
130   
131    if( _shaderList.size() < rhs._shaderList.size() ) return -1;
132    if( rhs._shaderList.size() < _shaderList.size() ) return 1;
133
134    if( getName() < rhs.getName() ) return -1;
135    if( rhs.getName() < getName() ) return 1;
136
137    if( _geometryVerticesOut < rhs._geometryVerticesOut ) return -1;
138    if( rhs._geometryVerticesOut < _geometryVerticesOut ) return 1;
139
140    if( _geometryInputType < rhs._geometryInputType ) return -1;
141    if( rhs._geometryInputType < _geometryInputType ) return 1;
142
143    if( _geometryOutputType < rhs._geometryOutputType ) return -1;
144    if( rhs._geometryOutputType < _geometryOutputType ) return 1;
145
146    ShaderList::const_iterator litr=_shaderList.begin();
147    ShaderList::const_iterator ritr=rhs._shaderList.begin();
148    for(;
149        litr!=_shaderList.end();
150        ++litr,++ritr)
151    {
152        int result = (*litr)->compare(*(*ritr));
153        if (result!=0) return result;
154    }
155
156    return 0; // passed all the above comparison macro's, must be equal.
157}
158
159
160void Program::compileGLObjects( osg::State& state ) const
161{
162    if( isFixedFunction() ) return;
163
164    const unsigned int contextID = state.getContextID();
165
166    for( unsigned int i=0; i < _shaderList.size(); ++i )
167    {
168        _shaderList[i]->compileShader( contextID );
169    }
170
171    getPCP( contextID )->linkProgram();
172}
173
174void Program::setThreadSafeRefUnref(bool threadSafe)
175{
176    StateAttribute::setThreadSafeRefUnref(threadSafe);
177
178    for( unsigned int i=0; i < _shaderList.size(); ++i )
179    {
180        if (_shaderList[i].valid()) _shaderList[i]->setThreadSafeRefUnref(threadSafe);
181    }
182}
183
184void Program::dirtyProgram()
185{
186    // mark our PCPs as needing relink
187    for( unsigned int cxt=0; cxt < _pcpList.size(); ++cxt )
188    {
189        if( _pcpList[cxt].valid() ) _pcpList[cxt]->requestLink();
190    }
191}
192
193
194void Program::resizeGLObjectBuffers(unsigned int maxSize)
195{
196    for( unsigned int i=0; i < _shaderList.size(); ++i )
197    {
198        if (_shaderList[i].valid()) _shaderList[i]->resizeGLObjectBuffers(maxSize);
199    }
200
201    _pcpList.resize(maxSize);
202}
203
204void Program::releaseGLObjects(osg::State* state) const
205{
206    for( unsigned int i=0; i < _shaderList.size(); ++i )
207    {
208        if (_shaderList[i].valid()) _shaderList[i]->releaseGLObjects(state);
209    }
210
211    if (!state) _pcpList.setAllElementsTo(0);
212    else
213    {
214        unsigned int contextID = state->getContextID();
215        _pcpList[contextID] = 0;
216    }   
217}
218
219
220bool Program::addShader( Shader* shader )
221{
222    if( !shader ) return false;
223
224    // Shader can only be added once to a Program
225    for( unsigned int i=0; i < _shaderList.size(); ++i )
226    {
227        if( shader == _shaderList[i].get() ) return false;
228    }
229
230    // Add shader to PCPs
231    for( unsigned int cxt=0; cxt < _pcpList.size(); ++cxt )
232    {
233        if( _pcpList[cxt].valid() ) _pcpList[cxt]->addShaderToAttach( shader );
234    }
235
236    shader->addProgramRef( this );
237    _shaderList.push_back( shader );
238    dirtyProgram();
239    return true;
240}
241
242
243bool Program::removeShader( Shader* shader )
244{
245    if( !shader ) return false;
246
247    // Shader must exist to be removed.
248    for( ShaderList::iterator itr = _shaderList.begin();
249         itr != _shaderList.end();
250         ++itr)
251    {
252        if( shader == itr->get() )
253        {
254            // Remove shader from PCPs
255            for( unsigned int cxt=0; cxt < _pcpList.size(); ++cxt )
256            {
257                if( _pcpList[cxt].valid() ) _pcpList[cxt]->addShaderToDetach( shader );
258            }
259
260            shader->removeProgramRef( this );
261            _shaderList.erase(itr);
262
263            dirtyProgram();
264            return true;
265        }
266    }
267
268    return false;
269}
270
271
272void Program::setParameter( GLenum pname, GLint value )
273{
274    switch( pname )
275    {
276        case GL_GEOMETRY_VERTICES_OUT_EXT:
277            _geometryVerticesOut = value;
278            dirtyProgram();
279            break;
280        case GL_GEOMETRY_INPUT_TYPE_EXT:
281            _geometryInputType = value;
282            dirtyProgram();    // needed?
283            break;
284        case GL_GEOMETRY_OUTPUT_TYPE_EXT:
285            _geometryOutputType = value;
286            dirtyProgram();    // needed?
287            break;
288        default:
289            osg::notify(osg::WARN) << "setParameter invalid param " << pname << std::endl;
290            break;
291    }
292}
293
294GLint Program::getParameter( GLenum pname ) const
295{
296    switch( pname )
297    {
298        case GL_GEOMETRY_VERTICES_OUT_EXT: return _geometryVerticesOut;
299        case GL_GEOMETRY_INPUT_TYPE_EXT:   return _geometryInputType;
300        case GL_GEOMETRY_OUTPUT_TYPE_EXTreturn _geometryOutputType;
301    }
302    osg::notify(osg::WARN) << "getParameter invalid param " << pname << std::endl;
303    return 0;
304}
305
306
307void Program::addBindAttribLocation( const std::string& name, GLuint index )
308{
309    _attribBindingList[name] = index;
310    dirtyProgram();
311}
312
313void Program::removeBindAttribLocation( const std::string& name )
314{
315    _attribBindingList.erase(name);
316    dirtyProgram();
317}
318
319void Program::addBindFragDataLocation( const std::string& name, GLuint index )
320{
321    _fragDataBindingList[name] = index;
322    dirtyProgram();
323}
324
325void Program::removeBindFragDataLocation( const std::string& name )
326{
327    _fragDataBindingList.erase(name);
328    dirtyProgram();
329}
330
331void Program::apply( osg::State& state ) const
332{
333    const unsigned int contextID = state.getContextID();
334    const GL2Extensions* extensions = GL2Extensions::Get(contextID,true);
335    if( ! extensions->isGlslSupported() ) return;
336
337    if( isFixedFunction() )
338    {
339        extensions->glUseProgram( 0 );
340        state.setLastAppliedProgramObject(0);
341        return;
342    }
343
344    PerContextProgram* pcp = getPCP( contextID );
345    if( pcp->needsLink() ) compileGLObjects( state );
346    if( pcp->isLinked() )
347    {
348        // for shader debugging: to minimize performance impact,
349        // optionally validate based on notify level.
350        // TODO: enable this using notify level, or perhaps its own getenv()?
351        if( osg::isNotifyEnabled(osg::INFO) )
352            pcp->validateProgram();
353
354        pcp->useProgram();
355        state.setLastAppliedProgramObject(pcp);
356    }
357    else
358    {
359        // program not usable, fallback to fixed function.
360        extensions->glUseProgram( 0 );
361        state.setLastAppliedProgramObject(0);
362    }
363}
364
365
366Program::PerContextProgram* Program::getPCP(unsigned int contextID) const
367{
368    if( ! _pcpList[contextID].valid() )
369    {
370        _pcpList[contextID] = new PerContextProgram( this, contextID );
371
372        // attach all PCSs to this new PCP
373        for( unsigned int i=0; i < _shaderList.size(); ++i )
374        {
375            _pcpList[contextID]->addShaderToAttach( _shaderList[i].get() );
376        }
377    }
378
379    return _pcpList[contextID].get();
380}
381
382
383bool Program::isFixedFunction() const
384{
385    // A Program object having no attached Shaders is a special case:
386    // it indicates that programmable shading is to be disabled,
387    // and thus use GL 1.x "fixed functionality" rendering.
388    return _shaderList.empty();
389}
390
391
392bool Program::getGlProgramInfoLog(unsigned int contextID, std::string& log) const
393{
394    return getPCP( contextID )->getInfoLog( log );
395}
396
397const Program::ActiveVarInfoMap& Program::getActiveUniforms(unsigned int contextID) const
398{
399    return getPCP( contextID )->getActiveUniforms();
400}
401
402const Program::ActiveVarInfoMap& Program::getActiveAttribs(unsigned int contextID) const
403{
404    return getPCP( contextID )->getActiveAttribs();
405}
406
407///////////////////////////////////////////////////////////////////////////
408// osg::Program::PerContextProgram
409// PCP is an OSG abstraction of the per-context glProgram
410///////////////////////////////////////////////////////////////////////////
411
412Program::PerContextProgram::PerContextProgram(const Program* program, unsigned int contextID ) :
413        osg::Referenced(),
414        _contextID( contextID )
415{
416    _program = program;
417    _extensions = GL2Extensions::Get( _contextID, true );
418    _glProgramHandle = _extensions->glCreateProgram();
419    requestLink();
420}
421
422Program::PerContextProgram::~PerContextProgram()
423{
424    Program::deleteGlProgram( _contextID, _glProgramHandle );
425}
426
427
428void Program::PerContextProgram::requestLink()
429{
430    _needsLink = true;
431    _isLinked = false;
432}
433
434
435void Program::PerContextProgram::linkProgram()
436{
437    if( ! _needsLink ) return;
438    _needsLink = false;
439
440    osg::notify(osg::INFO)
441        << "Linking osg::Program \"" << _program->getName() << "\""
442        << " id=" << _glProgramHandle
443        << " contextID=" << _contextID
444        <<  std::endl;
445
446    if (_extensions->isGeometryShader4Supported())
447    {
448        _extensions->glProgramParameteri( _glProgramHandle, GL_GEOMETRY_VERTICES_OUT_EXT, _program->_geometryVerticesOut );
449        _extensions->glProgramParameteri( _glProgramHandle, GL_GEOMETRY_INPUT_TYPE_EXT, _program->_geometryInputType );
450        _extensions->glProgramParameteri( _glProgramHandle, GL_GEOMETRY_OUTPUT_TYPE_EXT, _program->_geometryOutputType );
451    }
452   
453    // Detach removed shaders
454    for( unsigned int i=0; i < _shadersToDetach.size(); ++i )
455    {
456        _shadersToDetach[i]->detachShader( _contextID, _glProgramHandle );
457    }
458    _shadersToDetach.clear();
459   
460    // Attach new shaders
461    for( unsigned int i=0; i < _shadersToAttach.size(); ++i )
462    {
463        _shadersToAttach[i]->attachShader( _contextID, _glProgramHandle );
464    }
465    _shadersToAttach.clear();
466
467    _uniformInfoMap.clear();
468    _attribInfoMap.clear();
469    _lastAppliedUniformList.clear();
470
471    // set any explicit vertex attribute bindings
472    const AttribBindingList& bindlist = _program->getAttribBindingList();
473    for( AttribBindingList::const_iterator itr = bindlist.begin();
474        itr != bindlist.end(); ++itr )
475    {
476        _extensions->glBindAttribLocation( _glProgramHandle, itr->second, itr->first.c_str() );
477    }
478
479    // set any explicit frag data bindings
480    const FragDataBindingList& fdbindlist = _program->getFragDataBindingList();
481    for( FragDataBindingList::const_iterator itr = fdbindlist.begin();
482        itr != fdbindlist.end(); ++itr )
483    {
484        _extensions->glBindFragDataLocation( _glProgramHandle, itr->second, itr->first.c_str() );
485    }
486
487    // link the glProgram
488    GLint linked = GL_FALSE;
489    _extensions->glLinkProgram( _glProgramHandle );
490    _extensions->glGetProgramiv( _glProgramHandle, GL_LINK_STATUS, &linked );
491    _isLinked = (linked == GL_TRUE);
492    if( ! _isLinked )
493    {
494        osg::notify(osg::WARN) << "glLinkProgram \""<< _program->getName() << "\" FAILED" << std::endl;
495
496        std::string infoLog;
497        if( getInfoLog(infoLog) )
498        {
499            osg::notify(osg::WARN) << "Program \""<< _program->getName() << "\" " 
500                                      "infolog:\n" << infoLog << std::endl;
501        }
502       
503        return;
504    }
505    else
506    {
507        std::string infoLog;
508        if( getInfoLog(infoLog) )
509        {
510            osg::notify(osg::INFO) << "Program \""<< _program->getName() << "\" "<<
511                                      "link succeded, infolog:\n" << infoLog << std::endl;
512        }
513    }
514
515    // build _uniformInfoMap
516    GLint numUniforms = 0;
517    GLsizei maxLen = 0;
518    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_UNIFORMS, &numUniforms );
519    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen );
520    if( (numUniforms > 0) && (maxLen > 1) )
521    {
522        GLint size = 0;
523        GLenum type = 0;
524        GLchar* name = new GLchar[maxLen];
525
526        for( GLint i = 0; i < numUniforms; ++i )
527        {
528            _extensions->glGetActiveUniform( _glProgramHandle,
529                    i, maxLen, 0, &size, &type, name );
530
531            GLint loc = _extensions->glGetUniformLocation( _glProgramHandle, name );
532           
533            if( loc != -1 )
534            {
535                _uniformInfoMap[name] = ActiveVarInfo(loc,type,size);
536
537                osg::notify(osg::INFO)
538                    << "\tUniform \"" << name << "\""
539                    << " loc="<< loc
540                    << " size="<< size
541                    << " type=" << Uniform::getTypename((Uniform::Type)type)
542                    << std::endl;
543            }
544        }
545        delete [] name;
546    }
547
548    // build _attribInfoMap
549    GLint numAttrib = 0;
550    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_ATTRIBUTES, &numAttrib );
551    _extensions->glGetProgramiv( _glProgramHandle, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLen );
552    if( (numAttrib > 0) && (maxLen > 1) )
553    {
554        GLint size = 0;
555        GLenum type = 0;
556        GLchar* name = new GLchar[maxLen];
557
558        for( GLint i = 0; i < numAttrib; ++i )
559        {
560            _extensions->glGetActiveAttrib( _glProgramHandle,
561                    i, maxLen, 0, &size, &type, name );
562
563            GLint loc = _extensions->glGetAttribLocation( _glProgramHandle, name );
564           
565            if( loc != -1 )
566            {
567                _attribInfoMap[name] = ActiveVarInfo(loc,type,size);
568
569                osg::notify(osg::INFO)
570                    << "\tAttrib \"" << name << "\""
571                    << " loc=" << loc
572                    << " size=" << size
573                    << std::endl;
574            }
575        }
576        delete [] name;
577    }
578    osg::notify(osg::INFO) << std::endl;
579}
580
581bool Program::PerContextProgram::validateProgram()
582{
583    GLint validated = GL_FALSE;
584    _extensions->glValidateProgram( _glProgramHandle );
585    _extensions->glGetProgramiv( _glProgramHandle, GL_VALIDATE_STATUS, &validated );
586    if( validated == GL_TRUE)
587        return true;
588
589    osg::notify(osg::INFO)
590        << "glValidateProgram FAILED \"" << _program->getName() << "\""
591        << " id=" << _glProgramHandle
592        << " contextID=" << _contextID
593        <<  std::endl;
594
595    std::string infoLog;
596    if( getInfoLog(infoLog) )
597        osg::notify(osg::INFO) << "infolog:\n" << infoLog << std::endl;
598
599    osg::notify(osg::INFO) << std::endl;
600   
601    return false;
602}
603
604bool Program::PerContextProgram::getInfoLog( std::string& infoLog ) const
605{
606    return _extensions->getProgramInfoLog( _glProgramHandle, infoLog );
607}
608
609void Program::PerContextProgram::useProgram() const
610{
611    _extensions->glUseProgram( _glProgramHandle  );
612}
Note: See TracBrowser for help on using the browser.