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

Revision 13041, 16.8 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

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