root/OpenSceneGraph/trunk/src/osg/Shader.cpp @ 10760

Revision 10760, 14.6 kB (checked in by robert, 4 years ago)

Fixed Shader constructor

  • 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/Shader.cpp
17 * author: Mike Weiblen 2008-01-02
18*/
19
20#include <fstream>
21#include <list>
22#include <sstream>
23#include <iomanip>
24
25#include <osg/Notify>
26#include <osg/State>
27#include <osg/Timer>
28#include <osg/FrameStamp>
29#include <osg/buffered_value>
30#include <osg/ref_ptr>
31#include <osg/Shader>
32#include <osg/GLExtensions>
33
34#include <OpenThreads/ScopedLock>
35#include <OpenThreads/Mutex>
36
37using namespace osg;
38
39ShaderBinary::ShaderBinary()
40{
41}
42
43ShaderBinary::ShaderBinary(const ShaderBinary& rhs, const osg::CopyOp&):
44    _data(rhs._data)
45{
46}
47
48void ShaderBinary::allocate(unsigned int size)
49{
50    _data.clear();
51    _data.resize(size);
52}
53
54void ShaderBinary::assign(unsigned int size, const unsigned char* data)
55{
56    allocate(size);
57    if (data)
58    {
59        for(unsigned int i=0; i<size; ++i)
60        {
61            _data[i] = data[i];
62        }
63    }
64}
65
66        ///////////////////////////////////////////////////////////////////////////
67// static cache of glShaders flagged for deletion, which will actually
68// be deleted in the correct GL context.
69
70typedef std::list<GLuint> GlShaderHandleList;
71typedef osg::buffered_object<GlShaderHandleList> DeletedGlShaderCache;
72
73static OpenThreads::Mutex    s_mutex_deletedGlShaderCache;
74static DeletedGlShaderCache  s_deletedGlShaderCache;
75
76void Shader::deleteGlShader(unsigned int contextID, GLuint shader)
77{
78    if( shader )
79    {
80        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedGlShaderCache);
81
82        // add glShader to the cache for the appropriate context.
83        s_deletedGlShaderCache[contextID].push_back(shader);
84    }
85}
86
87void Shader::flushDeletedGlShaders(unsigned int contextID,double /*currentTime*/, double& availableTime)
88{
89    // if no time available don't try to flush objects.
90    if (availableTime<=0.0) return;
91
92    const GL2Extensions* extensions = GL2Extensions::Get(contextID,true);
93    if( ! extensions->isGlslSupported() ) return;
94
95    const osg::Timer& timer = *osg::Timer::instance();
96    osg::Timer_t start_tick = timer.tick();
97    double elapsedTime = 0.0;
98
99    {
100        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedGlShaderCache);
101
102        GlShaderHandleList& pList = s_deletedGlShaderCache[contextID];
103        for(GlShaderHandleList::iterator titr=pList.begin();
104            titr!=pList.end() && elapsedTime<availableTime;
105            )
106        {
107            extensions->glDeleteShader( *titr );
108            titr = pList.erase( titr );
109            elapsedTime = timer.delta_s(start_tick,timer.tick());
110        }
111    }
112
113    availableTime -= elapsedTime;
114}
115
116void Shader::discardDeletedGlShaders(unsigned int contextID)
117{
118    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedGlShaderCache);
119
120    GlShaderHandleList& pList = s_deletedGlShaderCache[contextID];
121    pList.clear();
122}
123
124///////////////////////////////////////////////////////////////////////////
125// osg::Shader
126///////////////////////////////////////////////////////////////////////////
127
128Shader::Shader(Type type) :
129    _type(type)
130{
131}
132
133Shader::Shader(Type type, const std::string& source) :
134    _type(type)
135{
136    setShaderSource( source);
137}
138
139Shader::Shader(Type type, ShaderBinary* shaderBinary) :
140    _type(type),
141    _shaderBinary(shaderBinary)
142{
143}
144
145
146Shader::Shader(const Shader& rhs, const osg::CopyOp& copyop):
147    osg::Object( rhs, copyop ),
148    _type(rhs._type),
149    _shaderFileName(rhs._shaderFileName),
150    _shaderSource(rhs._shaderSource),
151    _shaderBinary(rhs._shaderBinary)
152{
153}
154
155Shader::~Shader()
156{
157}
158
159bool Shader::setType(Type t)
160{
161    if (_type==t) return true;
162
163    if (_type != UNDEFINED)
164    {
165        osg::notify(osg::WARN) << "cannot change type of Shader" << std::endl;
166        return false;
167    }
168
169    _type = t;
170    return true;
171}
172
173int Shader::compare(const Shader& rhs) const
174{
175    if( this == &rhs ) return 0;
176
177    if( getType() < rhs.getType() ) return -1;
178    if( rhs.getType() < getType() ) return 1;
179
180    if( getName() < rhs.getName() ) return -1;
181    if( rhs.getName() < getName() ) return 1;
182
183    if( getShaderSource() < rhs.getShaderSource() ) return -1;
184    if( rhs.getShaderSource() < getShaderSource() ) return 1;
185
186    if( getFileName() < rhs.getFileName() ) return -1;
187    if( rhs.getFileName() < getFileName() ) return 1;
188    return 0;
189}
190
191void Shader::setShaderSource( const std::string& sourceText )
192{
193    _shaderSource = sourceText;
194    dirtyShader();
195}
196
197
198Shader* Shader::readShaderFile( Type type, const std::string& fileName )
199{
200    ref_ptr<Shader> shader = new Shader(type);
201    if (shader->loadShaderSourceFromFile(fileName)) return shader.release();
202    return 0;
203}
204
205bool Shader::loadShaderSourceFromFile( const std::string& fileName )
206{
207    std::ifstream sourceFile;
208
209    sourceFile.open(fileName.c_str(), std::ios::binary);
210    if(!sourceFile)
211    {
212        osg::notify(osg::WARN)<<"Error: can't open file \""<<fileName<<"\""<<std::endl;
213        return false;
214    }
215
216    osg::notify(osg::INFO)<<"Loading shader source file \""<<fileName<<"\""<<std::endl;
217    _shaderFileName = fileName;
218
219    sourceFile.seekg(0, std::ios::end);
220    int length = sourceFile.tellg();
221    char *text = new char[length + 1];
222    sourceFile.seekg(0, std::ios::beg);
223    sourceFile.read(text, length);
224    sourceFile.close();
225    text[length] = '\0';
226
227    setShaderSource( text );
228    delete [] text;
229    return true;
230}
231
232
233const char* Shader::getTypename() const
234{
235    switch( getType() )
236    {
237        case VERTEX:    return "VERTEX";
238        case FRAGMENTreturn "FRAGMENT";
239        case GEOMETRYreturn "GEOMETRY";
240        default:        return "UNDEFINED";
241    }
242}
243
244
245Shader::Type Shader::getTypeId( const std::string& tname )
246{
247    if( tname == "VERTEX" )     return VERTEX;
248    if( tname == "FRAGMENT" )   return FRAGMENT;
249    if( tname == "GEOMETRY" )   return GEOMETRY;
250    return UNDEFINED;
251}
252
253void Shader::resizeGLObjectBuffers(unsigned int maxSize)
254{
255    _pcsList.resize(maxSize);
256}
257
258void Shader::releaseGLObjects(osg::State* state) const
259{
260    if (!state) _pcsList.setAllElementsTo(0);
261    else
262    {
263        unsigned int contextID = state->getContextID();
264        _pcsList[contextID] = 0;
265    }
266}
267
268void Shader::compileShader( osg::State& state ) const
269{
270    PerContextShader* pcs = getPCS( state.getContextID() );
271    if( pcs ) pcs->compileShader( state );
272}
273
274
275Shader::PerContextShader* Shader::getPCS(unsigned int contextID) const
276{
277    if( getType() == UNDEFINED )
278    {
279        osg::notify(osg::WARN) << "Shader type is UNDEFINED" << std::endl;
280        return 0;
281    }
282
283    if( ! _pcsList[contextID].valid() )
284    {
285        _pcsList[contextID] = new PerContextShader( this, contextID );
286    }
287    return _pcsList[contextID].get();
288}
289
290
291void Shader::attachShader(unsigned int contextID, GLuint program) const
292{
293    PerContextShader* pcs = getPCS( contextID );
294    if( pcs ) pcs->attachShader( program );
295}
296
297void Shader::detachShader(unsigned int contextID, GLuint program) const
298{
299    PerContextShader* pcs = getPCS( contextID );
300    if( pcs ) pcs->detachShader( program );
301}
302
303
304bool Shader::getGlShaderInfoLog(unsigned int contextID, std::string& log) const
305{
306    PerContextShader* pcs = getPCS( contextID );
307    return (pcs) ? pcs->getInfoLog( log ) : false;
308}
309
310
311/////////////////////////////////////////////////////////////////////////
312// A Shader stores pointers to the osg::Programs to which it is attached,
313// so that if the Shader is marked for recompilation with
314// Shader::dirtyShader(), the upstream Program can be marked for relinking.
315// _programSet does not use ref_ptrs, as that would cause a cyclical
316// dependency, and neither the Program nor the Shader would be deleted.
317
318bool Shader::addProgramRef( Program* program )
319{
320    ProgramSet::iterator itr = _programSet.find(program);
321    if( itr != _programSet.end() ) return false;
322
323    _programSet.insert( program );
324    return true;
325}
326
327bool Shader::removeProgramRef( Program* program )
328{
329    ProgramSet::iterator itr = _programSet.find(program);
330    if( itr == _programSet.end() ) return false;
331
332    _programSet.erase( itr );
333    return true;
334}
335
336void Shader::dirtyShader()
337{
338    // Mark our PCSs as needing recompilation.
339    for( unsigned int cxt=0; cxt < _pcsList.size(); ++cxt )
340    {
341        if( _pcsList[cxt].valid() ) _pcsList[cxt]->requestCompile();
342    }
343
344    // Also mark Programs that depend on us as needing relink.
345    for( ProgramSet::iterator itr = _programSet.begin();
346        itr != _programSet.end(); ++itr )
347    {
348        (*itr)->dirtyProgram();
349    }
350}
351
352
353/////////////////////////////////////////////////////////////////////////
354// osg::Shader::PerContextShader
355// PCS is the OSG abstraction of the per-context glShader
356///////////////////////////////////////////////////////////////////////////
357
358Shader::PerContextShader::PerContextShader(const Shader* shader, unsigned int contextID) :
359        osg::Referenced(),
360        _contextID( contextID )
361{
362    _shader = shader;
363    _extensions = GL2Extensions::Get( _contextID, true );
364    _glShaderHandle = _extensions->glCreateShader( shader->getType() );
365    requestCompile();
366}
367
368
369Shader::PerContextShader::~PerContextShader()
370{
371    Shader::deleteGlShader( _contextID, _glShaderHandle );
372}
373
374
375void Shader::PerContextShader::requestCompile()
376{
377    _needsCompile = true;
378    _isCompiled = false;
379}
380
381namespace
382{
383    std::string insertLineNumbers(const std::string& source)
384    {
385        if (source.empty()) return source;
386
387        unsigned int lineNum = 1;       // Line numbers start at 1
388        std::ostringstream ostr;
389
390        std::string::size_type previous_pos = 0;
391        do
392        {
393            std::string::size_type pos = source.find_first_of("\n", previous_pos);
394            if (pos != std::string::npos)
395            {
396                ostr << std::setw(5)<<std::right<<lineNum<<": "<<source.substr(previous_pos, pos-previous_pos)<<std::endl;
397                previous_pos = pos+1<source.size() ? pos+1 : std::string::npos;
398            }
399            else
400            {
401                ostr << std::setw(5)<<std::right<<lineNum<<": "<<source.substr(previous_pos, std::string::npos)<<std::endl;
402                previous_pos = std::string::npos;
403            }
404            ++lineNum;
405
406        } while (previous_pos != std::string::npos);
407
408        return ostr.str();
409    }
410}
411
412void Shader::PerContextShader::compileShader(osg::State& state)
413{
414    if( ! _needsCompile ) return;
415    _needsCompile = false;
416
417#if defined(OSG_GLES2_AVAILABLE)
418    if (_shader->getShaderBinary())
419    {
420        GLint numFormats;
421        glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &numFormats);
422
423        if (numFormats>0)
424        {
425            GLint* formats = new GLint[numFormats];
426            glGetIntegerv(GL_SHADER_BINARY_FORMATS, formats);
427
428            for(GLint i=0; i<numFormats; ++i)
429            {
430                osg::notify(osg::NOTICE)<<"  format="<<formats[i]<<std::endl;
431                GLenum shaderBinaryFormat = formats[i];
432                glShaderBinary(1, &_glShaderHandle, shaderBinaryFormat, _shader->getShaderBinary()->getData(), _shader->getShaderBinary()->getSize());
433                if (glGetError() == GL_NO_ERROR)
434                {
435                    _isCompiled = true;
436                    return;
437                }
438            }
439            delete [] formats;
440           
441            if (_shader->getShaderSource().empty())
442            {
443                osg::notify(osg::WARN)<<"Warning: No suitable shader of supported format by GLES driver found in shader binary, unable to compile shader."<<std::endl;
444                _isCompiled = false;
445                return;
446            }
447            else
448            {
449                osg::notify(osg::NOTICE)<<"osg::Shader::compileShader(): No suitable shader of supported format by GLES driver found in shader binary, falling back to shader source."<<std::endl;
450            }
451        }
452        else
453        {
454            if (_shader->getShaderSource().empty())
455            {
456                osg::notify(osg::WARN)<<"Warning: No shader binary formats supported by GLES driver, unable to compile shader."<<std::endl;
457                _isCompiled = false;
458                return;
459            }
460            else
461            {
462                osg::notify(osg::NOTICE)<<"osg::Shader::compileShader(): No shader binary formats supported by GLES driver, falling back to shader source."<<std::endl;
463            }
464        }
465    }
466#endif
467
468    std::string source = _shader->getShaderSource();
469    if (_shader->getType()==osg::Shader::VERTEX && (state.getUseVertexAttributeAliasing() || state.getUseModelViewAndProjectionUniforms()))
470    {
471        state.convertVertexShaderSourceToOsgBuiltIns(source);
472    }
473
474    std::string sourceWithLineNumbers = insertLineNumbers(source);
475
476    if (osg::getNotifyLevel()>=osg::INFO)
477    {
478        osg::notify(osg::INFO)
479            << "\nCompiling " << _shader->getTypename()
480            << " source:\n" << sourceWithLineNumbers << std::endl;
481    }
482
483    GLint compiled = GL_FALSE;
484    const char* sourceText = source.c_str();
485    _extensions->glShaderSource( _glShaderHandle, 1, &sourceText, NULL );
486    _extensions->glCompileShader( _glShaderHandle );
487    _extensions->glGetShaderiv( _glShaderHandle, GL_COMPILE_STATUS, &compiled );
488
489    _isCompiled = (compiled == GL_TRUE);
490    if( ! _isCompiled )
491    {
492        osg::notify(osg::WARN) << _shader->getTypename() << " glCompileShader \""
493            << _shader->getName() << "\" FAILED" << std::endl;
494
495        std::string infoLog;
496        if( getInfoLog(infoLog) )
497        {
498            osg::notify(osg::WARN) << _shader->getTypename() << " Shader \""
499                << _shader->getName() << "\" infolog:\n" << infoLog << std::endl;
500        }
501    }
502    else
503    {
504        std::string infoLog;
505        if( getInfoLog(infoLog) )
506        {
507            osg::notify(osg::INFO) << _shader->getTypename() << " Shader \""
508                << _shader->getName() << "\" infolog:\n" << infoLog << std::endl;
509        }
510    }
511
512}
513
514bool Shader::PerContextShader::getInfoLog( std::string& infoLog ) const
515{
516    return _extensions->getShaderInfoLog( _glShaderHandle, infoLog );
517}
518
519void Shader::PerContextShader::attachShader(GLuint program) const
520{
521    _extensions->glAttachShader( program, _glShaderHandle );
522}
523
524void Shader::PerContextShader::detachShader(GLuint program) const
525{
526    _extensions->glDetachShader( program, _glShaderHandle );
527}
Note: See TracBrowser for help on using the browser.