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

Revision 10642, 12.1 kB (checked in by robert, 5 years ago)

Ported osg::Geometry across to supporting the aliasing of vertex, color and normal etc. calls to Vertex Attributes.

Added support for automatic aliasing of vertex, normal, color etc. arrays to Vertex Attribute equivelants.

Added new osg::GLBeginEndAdapter class for runtime conversion from glBegin/glEnd codes to vertex arrray equivelants.

Added automatic shader source conversion from gl_ to osg_ builtins.

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