Thanks J-S, I've rewritten the line number code to make it more
efficient (less creating/destroying of objects) and made the code path
for the debug output so that it's only run when when need it
(otherwise we just create a nicely parsed string for nothing.)  These
changes are now checked into svn/trunk.

On Mon, Jun 15, 2009 at 9:41 PM, Jean-Sébastien
Guay<[email protected]> wrote:
> Hello Robert,
>
> I was debugging shaders today and thought it would be nice if the shaders
> that are output to the console when they are compiled (at INFO level) were
> output with line numbers. Sometimes it's hard to see what line an error or
> warning refers to (say if the shader code is in a .cpp file). Warnings
> sometimes just say "implicit conversion from vec4 to vec3 without saying
> which variable names it refers to.
>
> So a pretty simple cosmetic change that just adds line numbers to the shader
> code printed to the console. You might want to place the code at the top of
> the file or format it differently (I've padded the numbers to 5 characters
> so we can go up to 99999 without affecting the code's alignment :-) ) but
> the essence is there.
>
> Thanks,
>
> J-S
> --
> ______________________________________________________
> Jean-Sebastien Guay    [email protected]
>                               http://www.cm-labs.com/
>                        http://whitestar02.webhop.org/
>
> /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
>  * Copyright (C) 2003-2005 3Dlabs Inc. Ltd.
>  * Copyright (C) 2004-2005 Nathan Cournia
>  * Copyright (C) 2008 Zebra Imaging
>  *
>  * This application is open source and may be redistributed and/or modified
>  * freely and without restriction, both in commericial and non commericial
>  * applications, as long as this copyright notice is maintained.
>  *
>  * This application is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>  *
> */
>
> /* file:   src/osg/Shader.cpp
>  * author: Mike Weiblen 2008-01-02
> */
>
> #include <fstream>
> #include <list>
> #include <sstream>
> #include <iomanip>
>
> #include <osg/Notify>
> #include <osg/State>
> #include <osg/Timer>
> #include <osg/FrameStamp>
> #include <osg/buffered_value>
> #include <osg/ref_ptr>
> #include <osg/Shader>
> #include <osg/GLExtensions>
>
> #include <OpenThreads/ScopedLock>
> #include <OpenThreads/Mutex>
>
> using namespace osg;
>
> ///////////////////////////////////////////////////////////////////////////
> // static cache of glShaders flagged for deletion, which will actually
> // be deleted in the correct GL context.
>
> typedef std::list<GLuint> GlShaderHandleList;
> typedef osg::buffered_object<GlShaderHandleList> DeletedGlShaderCache;
>
> static OpenThreads::Mutex    s_mutex_deletedGlShaderCache;
> static DeletedGlShaderCache  s_deletedGlShaderCache;
>
> void Shader::deleteGlShader(unsigned int contextID, GLuint shader)
> {
>    if( shader )
>    {
>        OpenThreads::ScopedLock<OpenThreads::Mutex>
> lock(s_mutex_deletedGlShaderCache);
>
>        // add glShader to the cache for the appropriate context.
>        s_deletedGlShaderCache[contextID].push_back(shader);
>    }
> }
>
> void Shader::flushDeletedGlShaders(unsigned int contextID,double
> /*currentTime*/, double& availableTime)
> {
>    // if no time available don't try to flush objects.
>    if (availableTime<=0.0) return;
>
>    const GL2Extensions* extensions = GL2Extensions::Get(contextID,true);
>    if( ! extensions->isGlslSupported() ) return;
>
>    const osg::Timer& timer = *osg::Timer::instance();
>    osg::Timer_t start_tick = timer.tick();
>    double elapsedTime = 0.0;
>
>    {
>        OpenThreads::ScopedLock<OpenThreads::Mutex>
> lock(s_mutex_deletedGlShaderCache);
>
>        GlShaderHandleList& pList = s_deletedGlShaderCache[contextID];
>        for(GlShaderHandleList::iterator titr=pList.begin();
>            titr!=pList.end() && elapsedTime<availableTime;
>            )
>        {
>            extensions->glDeleteShader( *titr );
>            titr = pList.erase( titr );
>            elapsedTime = timer.delta_s(start_tick,timer.tick());
>        }
>    }
>
>    availableTime -= elapsedTime;
> }
>
> void Shader::discardDeletedGlShaders(unsigned int contextID)
> {
>    OpenThreads::ScopedLock<OpenThreads::Mutex>
> lock(s_mutex_deletedGlShaderCache);
>
>    GlShaderHandleList& pList = s_deletedGlShaderCache[contextID];
>    pList.clear();
> }
>
> ///////////////////////////////////////////////////////////////////////////
> // osg::Shader
> ///////////////////////////////////////////////////////////////////////////
>
> Shader::Shader(Type type) :
>    _type(type)
> {
> }
>
> Shader::Shader(Type type, const std::string& source) :
>    _type(type)
> {
>    setShaderSource( source);
> }
>
> Shader::Shader(const Shader& rhs, const osg::CopyOp& copyop):
>    osg::Object( rhs, copyop ),
>    _type(rhs._type),
>    _shaderSource(rhs._shaderSource),
>    _shaderFileName(rhs._shaderFileName)
> {
> }
>
> Shader::~Shader()
> {
> }
>
> bool Shader::setType( Type t )
> {
>    if (_type==t) return true;
>
>    if (_type != UNDEFINED)
>    {
>        osg::notify(osg::WARN) << "cannot change type of Shader" <<
> std::endl;
>        return false;
>    }
>
>    _type = t;
>    return true;
> }
>
> int Shader::compare(const Shader& rhs) const
> {
>    if( this == &rhs ) return 0;
>
>    if( getType() < rhs.getType() ) return -1;
>    if( rhs.getType() < getType() ) return 1;
>
>    if( getName() < rhs.getName() ) return -1;
>    if( rhs.getName() < getName() ) return 1;
>
>    if( getShaderSource() < rhs.getShaderSource() ) return -1;
>    if( rhs.getShaderSource() < getShaderSource() ) return 1;
>
>    if( getFileName() < rhs.getFileName() ) return -1;
>    if( rhs.getFileName() < getFileName() ) return 1;
>    return 0;
> }
>
> void Shader::setShaderSource( const std::string& sourceText )
> {
>    _shaderSource = sourceText;
>    dirtyShader();
> }
>
>
> Shader* Shader::readShaderFile( Type type, const std::string& fileName )
> {
>    ref_ptr<Shader> shader = new Shader(type);
>    if (shader->loadShaderSourceFromFile(fileName)) return shader.release();
>    return 0;
> }
>
> bool Shader::loadShaderSourceFromFile( const std::string& fileName )
> {
>    std::ifstream sourceFile;
>
>    sourceFile.open(fileName.c_str(), std::ios::binary);
>    if(!sourceFile)
>    {
>        osg::notify(osg::WARN)<<"Error: can't open file
> \""<<fileName<<"\""<<std::endl;
>        return false;
>    }
>
>    osg::notify(osg::INFO)<<"Loading shader source file
> \""<<fileName<<"\""<<std::endl;
>    _shaderFileName = fileName;
>
>    sourceFile.seekg(0, std::ios::end);
>    int length = sourceFile.tellg();
>    char *text = new char[length + 1];
>    sourceFile.seekg(0, std::ios::beg);
>    sourceFile.read(text, length);
>    sourceFile.close();
>    text[length] = '\0';
>
>    setShaderSource( text );
>    delete [] text;
>    return true;
> }
>
>
> const char* Shader::getTypename() const
> {
>    switch( getType() )
>    {
>        case VERTEX:    return "VERTEX";
>        case FRAGMENT:  return "FRAGMENT";
>        case GEOMETRY:  return "GEOMETRY";
>        default:        return "UNDEFINED";
>    }
> }
>
>
> Shader::Type Shader::getTypeId( const std::string& tname )
> {
>    if( tname == "VERTEX" )     return VERTEX;
>    if( tname == "FRAGMENT" )   return FRAGMENT;
>    if( tname == "GEOMETRY" )   return GEOMETRY;
>    return UNDEFINED;
> }
>
> void Shader::resizeGLObjectBuffers(unsigned int maxSize)
> {
>    _pcsList.resize(maxSize);
> }
>
> void Shader::releaseGLObjects(osg::State* state) const
> {
>    if (!state) _pcsList.setAllElementsTo(0);
>    else
>    {
>        unsigned int contextID = state->getContextID();
>        _pcsList[contextID] = 0;
>    }
> }
>
> void Shader::compileShader( unsigned int contextID ) const
> {
>    PerContextShader* pcs = getPCS( contextID );
>    if( pcs ) pcs->compileShader();
> }
>
>
> Shader::PerContextShader* Shader::getPCS(unsigned int contextID) const
> {
>    if( getType() == UNDEFINED )
>    {
>        osg::notify(osg::WARN) << "Shader type is UNDEFINED" << std::endl;
>        return 0;
>    }
>
>    if( ! _pcsList[contextID].valid() )
>    {
>        _pcsList[contextID] = new PerContextShader( this, contextID );
>    }
>    return _pcsList[contextID].get();
> }
>
>
> void Shader::attachShader(unsigned int contextID, GLuint program) const
> {
>    PerContextShader* pcs = getPCS( contextID );
>    if( pcs ) pcs->attachShader( program );
> }
>
> void Shader::detachShader(unsigned int contextID, GLuint program) const
> {
>    PerContextShader* pcs = getPCS( contextID );
>    if( pcs ) pcs->detachShader( program );
> }
>
>
> bool Shader::getGlShaderInfoLog(unsigned int contextID, std::string& log)
> const
> {
>    PerContextShader* pcs = getPCS( contextID );
>    return (pcs) ? pcs->getInfoLog( log ) : false;
> }
>
>
> /////////////////////////////////////////////////////////////////////////
> // A Shader stores pointers to the osg::Programs to which it is attached,
> // so that if the Shader is marked for recompilation with
> // Shader::dirtyShader(), the upstream Program can be marked for relinking.
> // _programSet does not use ref_ptrs, as that would cause a cyclical
> // dependency, and neither the Program nor the Shader would be deleted.
>
> bool Shader::addProgramRef( Program* program )
> {
>    ProgramSet::iterator itr = _programSet.find(program);
>    if( itr != _programSet.end() ) return false;
>
>    _programSet.insert( program );
>    return true;
> }
>
> bool Shader::removeProgramRef( Program* program )
> {
>    ProgramSet::iterator itr = _programSet.find(program);
>    if( itr == _programSet.end() ) return false;
>
>    _programSet.erase( itr );
>    return true;
> }
>
> void Shader::dirtyShader()
> {
>    // Mark our PCSs as needing recompilation.
>    for( unsigned int cxt=0; cxt < _pcsList.size(); ++cxt )
>    {
>        if( _pcsList[cxt].valid() ) _pcsList[cxt]->requestCompile();
>    }
>
>    // Also mark Programs that depend on us as needing relink.
>    for( ProgramSet::iterator itr = _programSet.begin();
>        itr != _programSet.end(); ++itr )
>    {
>        (*itr)->dirtyProgram();
>    }
> }
>
>
> /////////////////////////////////////////////////////////////////////////
> // osg::Shader::PerContextShader
> // PCS is the OSG abstraction of the per-context glShader
> ///////////////////////////////////////////////////////////////////////////
>
> Shader::PerContextShader::PerContextShader(const Shader* shader, unsigned
> int contextID) :
>        osg::Referenced(),
>        _contextID( contextID )
> {
>    _shader = shader;
>    _extensions = GL2Extensions::Get( _contextID, true );
>    _glShaderHandle = _extensions->glCreateShader( shader->getType() );
>    requestCompile();
> }
>
>
> Shader::PerContextShader::~PerContextShader()
> {
>    Shader::deleteGlShader( _contextID, _glShaderHandle );
> }
>
>
> void Shader::PerContextShader::requestCompile()
> {
>    _needsCompile = true;
>    _isCompiled = false;
> }
>
> namespace
> {
>    std::string formatLineNumber(unsigned int num)
>    {
>        std::ostringstream ostr;
>        ostr << std::setw(5) << std::right << num;
>        return ostr.str();
>    }
>
>    std::string insertLineNumbers(const std::string& source)
>    {
>        std::string sourceWithLineNumbers(source);
>
>        std::string::size_type pos = 0;
>        unsigned int lineNum = 1;       // Line numbers start at 1
>        while (pos != std::string::npos)
>        {
>            sourceWithLineNumbers = sourceWithLineNumbers.substr(0, pos) +
>                                    (pos > 0 ? "\n" : "") +
>   // First number will be on first line so no \n
>                                    formatLineNumber(lineNum) + " " +
>                                    sourceWithLineNumbers.substr(pos+1);
>
>            // Find next line break.
>            pos = sourceWithLineNumbers.find_first_of("\n", pos+1);
>            ++lineNum;
>        }
>
>        return sourceWithLineNumbers;
>    }
> }
>
> void Shader::PerContextShader::compileShader()
> {
>    if( ! _needsCompile ) return;
>    _needsCompile = false;
>
>    std::string sourceWithLineNumbers =
> insertLineNumbers(_shader->getShaderSource());
>    osg::notify(osg::INFO)
>        << "\nCompiling " << _shader->getTypename()
>        << " source:\n" << sourceWithLineNumbers << std::endl;
>
>    GLint compiled = GL_FALSE;
>    const char* sourceText = _shader->getShaderSource().c_str();
>    _extensions->glShaderSource( _glShaderHandle, 1, &sourceText, NULL );
>    _extensions->glCompileShader( _glShaderHandle );
>    _extensions->glGetShaderiv( _glShaderHandle, GL_COMPILE_STATUS, &compiled
> );
>
>    _isCompiled = (compiled == GL_TRUE);
>    if( ! _isCompiled )
>    {
>        osg::notify(osg::WARN) << _shader->getTypename() << " glCompileShader
> \""
>            << _shader->getName() << "\" FAILED" << std::endl;
>
>        std::string infoLog;
>        if( getInfoLog(infoLog) )
>        {
>            osg::notify(osg::WARN) << _shader->getTypename() << " Shader \""
>                << _shader->getName() << "\" infolog:\n" << infoLog <<
> std::endl;
>        }
>    }
>    else
>    {
>        std::string infoLog;
>        if( getInfoLog(infoLog) )
>        {
>            osg::notify(osg::INFO) << _shader->getTypename() << " Shader \""
>                << _shader->getName() << "\" infolog:\n" << infoLog <<
> std::endl;
>        }
>    }
>
> }
>
> bool Shader::PerContextShader::getInfoLog( std::string& infoLog ) const
> {
>    return _extensions->getShaderInfoLog( _glShaderHandle, infoLog );
> }
>
> void Shader::PerContextShader::attachShader(GLuint program) const
> {
>    _extensions->glAttachShader( program, _glShaderHandle );
> }
>
> void Shader::PerContextShader::detachShader(GLuint program) const
> {
>    _extensions->glDetachShader( program, _glShaderHandle );
> }
>
> _______________________________________________
> osg-submissions mailing list
> [email protected]
> http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
>
>
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to