Hi Robert

I change in shader  "#version 420 compatibility" by required extensions
I change the hardware support test like in osgVolume


I try to optimize osgatomiccounter example in many way but I haven't got
better result.
- I try to disable VSync, didn't change the frame time,
- I try to do double/triple/... buffering of atomic counter buffer, didn't
change the frame time,

To resume,  N=number of buffer
with N=2 ot N=3
if I write/use the Buffer N and I read the buffer N, Draw time ~= GPU time
I suppose application have to wait GL finish its work before read the buffer

if I write/use the Buffer N and I read the buffer N-1, Draw time ~= GPU time
well, application is not blocked by GL, so what happen ?

if I write/use the Buffer N and I read the buffer N+1, Draw time ~= GPU time
same comment

with N=4 and above
if I write/use the Buffer N and I read the buffer N+1, Draw time ~= 0,35
and GPU time ~= 14.5
so application can easily read the buffer, but GL take a while to bind
buffer for write/use in next frame.



So if my brain is not too crazy after so many test,
Atomic counter buffer take a while to swap between read and write mode.
Read a buffer used in current frame or in previous frame take a while.
Read a buffer used 3 frame ago or above is really fast but next bind will
take a while.

To reproduce my test, export OSG_NOTIFY_LEVEL=INFO and use options
  --num-buffer      Number of buffer use in buffer ring for read/write
usage.
  --num-read        Number of buffer readback in FinalDrawCallback.
  --offset          Number of buffer in buffer ring between buffer
write/use in
                    shader and buffer readback in FinalDrawCallback.


Perhaps an example without all this test stuff will be better to show howto
use Atomic Counter Buffer.
Thoughts ?


Cheers
David

2012/3/29 Robert Osfield <[email protected]>

> Hi David,
>
> I have now merged and checked in all your changes, the OSG now support
> atomic counter buffer ;-)
>
> Robert.
> _______________________________________________
> osg-submissions mailing list
> [email protected]
>
> http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
>
/* -*-c++-*- OpenSceneGraph - Copyright (C) 2012-2012 David Callu
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commercial and non commercial 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.
*/

#include <vector>

#include <osg/BufferIndexBinding>
#include <osg/BufferObject>
#include <osg/Camera>
#include <osg/Program>

#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/StateSetManipulator>
#include <osgGA/AnimationPathManipulator>
#include <osgGA/TerrainManipulator>
#include <osgGA/SphericalManipulator>

#include <iostream>

typedef std::vector< osg::ref_ptr<osg::AtomicCounterBufferBinding> > AtomicCounterBufferBindingList;
typedef std::vector< osg::ref_ptr<osg::UIntArray> > UIntArrayList;

class TestSupportOperation: public osg::GraphicsOperation
{
public:

    TestSupportOperation():
        osg::GraphicsOperation("TestSupportOperation",false),
        _supported(true),
        _errorMessage() {}

    virtual void operator () (osg::GraphicsContext* gc)
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

        osg::GL2Extensions & ext = *osg::GL2Extensions::Get(gc->getState()->getContextID(), true);

        if (ext.isShaderAtomicCounterSupported() == false)
        {
            _errorMessage = "osgatomiccounter : GL_ARB_shader_atomic_counters not supported. abord.";
            _supported = false;
        }

        if (ext.isShaderAtomicCounterSupported() == false)
        {
            _errorMessage = "osgatomiccounter : GL_ARB_uniform_buffer_object not supported. abord.";
            _supported = false;
        }


    }

    OpenThreads::Mutex  _mutex;
    bool                _supported;
    std::string         _errorMessage;
};


class AdaptNumPixelUniform : public osg::Camera::DrawCallback
{
    public:
        AdaptNumPixelUniform()
        {
            _numBufferToRead = 1;
            _atomicCounterArray = new osg::UIntArray;
            _atomicCounterArray->push_back(0);
        }

        virtual void operator () (osg::RenderInfo& renderInfo) const
        {
            for (unsigned int i=0; i<_numBufferToRead; ++i)
            {
                unsigned int index = (renderInfo.getState()->getFrameStamp()->getFrameNumber()+i) % _acbbList.size();
                osg::Timer timer;
                _acbbList[index]->readData(*renderInfo.getState(), *_atomicCounterArray);
                OSG_INFO << "osgatomiccounter : iteration "<<i<<" : readback from atomic counter buffer index=\""<<index<<"\" take "<<timer.time_u()<<" us."<<std::endl;
            }

            unsigned int numPixel = osg::maximum(1u, _atomicCounterArray->front());

            if ((renderInfo.getView()->getFrameStamp()->getFrameNumber() % 10) == 0)
            {
                OSG_INFO << "osgatomiccounter : draw " << numPixel << " pixels." << std::endl;
            }

            _invNumPixelUniform->set( 1.0f / static_cast<float>(numPixel) );
        }

        unsigned int _numBufferToRead;
        osg::ref_ptr<osg::Uniform> _invNumPixelUniform;
        osg::ref_ptr<osg::UIntArray> _atomicCounterArray;
        AtomicCounterBufferBindingList _acbbList;
};

class BufferSwapCallback : public osg::StateSet::Callback
{
    public:
        BufferSwapCallback() : _offset(3) {}

        virtual void operator() (osg::StateSet * ss, osg::NodeVisitor * nv)
        {
            if (_prevAcbb)
                ss->removeAttribute(_prevAcbb.get());

            unsigned int index = (nv->getFrameStamp()->getFrameNumber()+_offset) % _acbbList.size();
            _prevAcbb = _acbbList[index];
            ss->setAttributeAndModes(_prevAcbb);

            OSG_INFO << "\n\nosgatomiccounter : reset and use atomic counter buffer index=\""<<index<<"\""<<std::endl;
        }

        unsigned int _offset;
        osg::ref_ptr<osg::AtomicCounterBufferBinding> _prevAcbb;
        AtomicCounterBufferBindingList _acbbList;
};


osg::Program * createProgram()
{
    std::stringstream vp;
    vp << "#extension GL_ARB_compatibility : enable\n"
       << "\n"
       << "void main(void)\n"
       << "{\n"
       << "    gl_Position = ftransform();\n"
       << "}\n";
    osg::Shader * vpShader = new osg::Shader( osg::Shader::VERTEX, vp.str() );



    std::stringstream fp;
    fp << "#extension GL_ARB_compatibility : enable\n"
       << "#extension GL_ARB_uniform_buffer_object : enable\n"
       << "#extension GL_ARB_shader_atomic_counters : enable\n"
       << "\n"
       << "layout(binding = 0) uniform atomic_uint acRed;\n"
       << "layout(binding = 0, offset = 4) uniform atomic_uint acGreen;\n"
       << "layout(binding = 2) uniform atomic_uint acBlue;\n"
       << "\n"
       << "uniform float invNumPixel;\n"
       << "\n"
       << "void main(void)\n"
       << "{\n"
       << "    float r = float(atomicCounterIncrement(acRed)) * invNumPixel;\n"
       << "    float g = float(atomicCounterIncrement(acGreen)) * invNumPixel;\n"
       << "    float b = float(atomicCounterIncrement(acBlue)) * invNumPixel;\n"
       << "    gl_FragColor = vec4(r, g, b, 1.0);\n"
       << "}\n"
       << "\n";
    osg::Shader * fpShader = new osg::Shader( osg::Shader::FRAGMENT, fp.str() );

    osg::Program * program = new osg::Program;
    program->addShader(vpShader);
    program->addShader(fpShader);

    return program;
}

class ResetAtomicCounter : public osg::StateAttributeCallback
{
    public:
        virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor*)
        {
            osg::AtomicCounterBufferBinding * acbb = dynamic_cast<osg::AtomicCounterBufferBinding *>(sa);
            if (acbb)
            {
                osg::AtomicCounterBufferObject * acbo = dynamic_cast<osg::AtomicCounterBufferObject*>(acbb->getBufferObject());
                if (acbo && acbo->getBufferData(0))
                {
                    acbo->getBufferData(0)->dirty();
                }
            }
        }
};


osg::AtomicCounterBufferBinding * createAtomicCounterBufferBinding(unsigned int bindIndex, unsigned int size, UIntArrayList & uIntArrayList)
{
    // create atomic counter array
    osg::ref_ptr<osg::UIntArray> atomicCounterArray = new osg::UIntArray;
    for (unsigned int i=0; i<size;++i)
        atomicCounterArray->push_back(0);

    // save sourceArray
    uIntArrayList.push_back(atomicCounterArray);


    osg::ref_ptr<osg::AtomicCounterBufferObject> acbo = new osg::AtomicCounterBufferObject;
    acbo->setUsage(GL_STREAM_COPY);
    atomicCounterArray->setBufferObject(acbo.get());

    osg::ref_ptr<osg::AtomicCounterBufferBinding> acbb = new osg::AtomicCounterBufferBinding(bindIndex, acbo.get(), 0, sizeof(GLuint)*size);

    // add callback to reset atomic counter value
    acbb->setUpdateCallback(new ResetAtomicCounter);

    return acbb.release();
}

int main(int argc, char** argv)
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is a simple example which show draw order of pixel.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("--num-buffer", "Number of buffer use in buffer ring for read/write usage.");
    arguments.getApplicationUsage()->addCommandLineOption("--offset", "Number of buffer in buffer ring between buffer write/use in shader and buffer readback in FinalDrawCallback.");
    arguments.getApplicationUsage()->addCommandLineOption("--num-read", "Number of buffer readback in FinalDrawCallback.");

    unsigned int numBuffer = 2;
    arguments.read("--num-buffer", numBuffer);

    unsigned int offset = numBuffer-1;
    arguments.read("--offset", offset);
    if (offset >= numBuffer) offset = numBuffer-1;

    unsigned int numBufferToRead = 1;
    arguments.read("--num-read", numBufferToRead);
    if (numBufferToRead > numBuffer) numBufferToRead = numBuffer;


    osgViewer::Viewer viewer(arguments);

    unsigned int helpType = 0;
    if ((helpType = arguments.readHelpType()))
    {
        arguments.getApplicationUsage()->write(std::cout, helpType);
        return 1;
    }

    // report any errors if they have occurred when parsing the program arguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }

    // set up the camera manipulators.
    viewer.setCameraManipulator( new osgGA::TrackballManipulator() );

    // add the state manipulator
    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );

    // add the thread model handler
    viewer.addEventHandler(new osgViewer::ThreadingHandler);

    // add the window size toggle handler
    viewer.addEventHandler(new osgViewer::WindowSizeHandler);

    // add the stats handler
    viewer.addEventHandler(new osgViewer::StatsHandler);

    // add the help handler
    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));

    // add the screen capture handler
    viewer.addEventHandler(new osgViewer::ScreenCaptureHandler);

    // load the data
    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
    if (!loadedModel)
    {
        osg::Geometry * quad = osg::createTexturedQuadGeometry(osg::Vec3f(-2.0f, 0.0f, -2.0f),
                                                          osg::Vec3f(2.0f, 0.0f, 0.0f),
                                                          osg::Vec3f(0.0f, 0.0f, 2.0f) );

        osg::Geode * geode = new osg::Geode;
        geode->addDrawable(quad);
        loadedModel = geode;
    }

    // any option left unread are converted into errors to write out later.
    arguments.reportRemainingOptionsAsUnrecognized();

    // report any errors if they have occurred when parsing the program arguments.
    if (arguments.errors())
    {
        arguments.writeErrorMessages(std::cout);
        return 1;
    }


    osg::StateSet * ss = loadedModel->asGeode()->getDrawable(0)->getOrCreateStateSet();
    ss->setAttributeAndModes( createProgram(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED );


    UIntArrayList uIntArrayList;
    ss = loadedModel->getOrCreateStateSet();

    // create atomic counter buffer binding and add them to StateSet
    osg::ref_ptr<osg::AtomicCounterBufferBinding> acbbRedAndGreen = createAtomicCounterBufferBinding(0, 2, uIntArrayList);
    ss->setAttributeAndModes(acbbRedAndGreen.get());


    AtomicCounterBufferBindingList acbbList;
    for (unsigned int i=0; i<numBuffer; ++i)
    {
        acbbList.push_back( createAtomicCounterBufferBinding(2, 1, uIntArrayList) );
    }


    // add callback to do double buffering with atomic counter buffer to speedup readback value on each frame
    BufferSwapCallback * bufferSwapCallback = new BufferSwapCallback;
    bufferSwapCallback->_acbbList = acbbList;
    bufferSwapCallback->_offset = offset;
    ss->setUpdateCallback(bufferSwapCallback);


    osg::ref_ptr<osg::Uniform> invNumPixelUniform = new osg::Uniform("invNumPixel", 1.0f/(800.0f*600.0f));
    ss->addUniform( invNumPixelUniform.get() );



    // create callback to adapt number of pixel draw in one frame, value is store in uniform and used by shader
    AdaptNumPixelUniform * adaptNumPixelUniformCallback = new AdaptNumPixelUniform;
    adaptNumPixelUniformCallback->_numBufferToRead = numBufferToRead;
    adaptNumPixelUniformCallback->_invNumPixelUniform = invNumPixelUniform;
    adaptNumPixelUniformCallback->_acbbList = acbbList;

    viewer.getCamera()->setFinalDrawCallback(adaptNumPixelUniformCallback);




    // optimize the scene graph, remove redundant nodes and state etc.
    osgUtil::Optimizer optimizer;
    optimizer.optimize(loadedModel.get());

    viewer.setSceneData( loadedModel.get() );


    osg::ref_ptr<TestSupportOperation> testSupportOperation = new TestSupportOperation;
    viewer.setRealizeOperation(testSupportOperation.get());

    viewer.realize();

    if ( ! testSupportOperation->_supported )
    {
        osg::notify(osg::WARN)<<testSupportOperation->_errorMessage<<std::endl;
        return 1;
    }

    return viewer.run();
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to