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