From 1b6e50a2dd080fcdb8785059d150a5aef4100246 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 10 Dec 2014 12:23:04 +0000 Subject: [PATCH] From Marcus Hein, Added support for OpenGL SSBO and SSBB via osg::ShaderStorageBufferObject and osg::ShaderStorageBufferBinding to core OSG library, and added new osgSSBO example git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14599 16af8721-9629-0410-8352-f15c8da7e697 --- examples/CMakeLists.txt | 1 + examples/osgSSBO/CMakeLists.txt | 4 + examples/osgSSBO/osgSSBO.cpp | 945 ++++++++++++++++++++++++++++++++ include/osg/BufferIndexBinding | 29 + include/osg/BufferObject | 15 + include/osg/StateAttribute | 2 + src/osg/BufferIndexBinding.cpp | 22 + src/osg/BufferObject.cpp | 22 + 8 files changed, 1040 insertions(+) create mode 100644 examples/osgSSBO/CMakeLists.txt create mode 100644 examples/osgSSBO/osgSSBO.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d2d383da3..b744338ee 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -97,6 +97,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgpresentation) ADD_SUBDIRECTORY(osgreflect) ADD_SUBDIRECTORY(osgrobot) + ADD_SUBDIRECTORY(osgSSBO) ADD_SUBDIRECTORY(osgscalarbar) ADD_SUBDIRECTORY(osgscribe) ADD_SUBDIRECTORY(osgsequence) diff --git a/examples/osgSSBO/CMakeLists.txt b/examples/osgSSBO/CMakeLists.txt new file mode 100644 index 000000000..d82273b23 --- /dev/null +++ b/examples/osgSSBO/CMakeLists.txt @@ -0,0 +1,4 @@ + +SET(TARGET_SRC osgSSBO.cpp ) + +SETUP_EXAMPLE(osgSSBO) diff --git a/examples/osgSSBO/osgSSBO.cpp b/examples/osgSSBO/osgSSBO.cpp new file mode 100644 index 000000000..9c91bbfa6 --- /dev/null +++ b/examples/osgSSBO/osgSSBO.cpp @@ -0,0 +1,945 @@ +//info : osgSSBO example,testing ShaderStorageBufferObjects ,Markus Hein, 2014, osg-3.2.1 +//required hardware and driver must support GL >= GL 4.3 or GL ES 3.1 (GL ES not tested, would be nice if someone will test it on a small device) + +//testing osg support for Shader Storage Buffer Objects + +//version: "first take" from last night session.. + + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include +#include +#include +#include +#include +#include + + +using namespace osg; + + +//todo .. #define COMPUTATION_IN_SEPARATE_THREAD + +#define WORK_GROUP_SIZE 16 + + +#define PRERENDER_ANTIALIASINGMULTISAMPLES 16 +#define PRERENDER_HIGH_QUALITY_ANTIALIASING +#define PRERENDER_WIDTH 1920 +#define PRERENDER_HEIGHT 1080 + + +#define SUB_PLACEMENT_OFFSET_HORIZONTAL 0.5 +#define SUB_PLACEMENT_OFFSET_VERTICAL 0.5 + +enum BufferOffset +{ + POSITION_NOW_OFFSET, + POSITION_OLD_OFFSET, + POSITION_INIT_OFFSET, + + VELOCITY_NOW_OFFSET, + VELOCITY_OLD_OFFSET, + VELOCITY_INIT_OFFSET, + + ACCELERATION_OFFSET, + PROPERTIES_OFFSET, + + OFFSET_END +}; + + +const int __numDataValuesPerChannel = OFFSET_END; +const int __numChannels = 4; +const float __particleRenderScaleMultiplier = 0.3; + +//512x512x4x7 = 7.340.032 floats in SSBO on GPU +const int NUM_ELEMENTS_X = 512; +const int NUM_ELEMENTS_Y = 512; + +float random(float min, float max) { return min + (max - min)*(float)rand() / (float)RAND_MAX; } + + +enum Channel +{ + RED_CHANNEL, + GREEN_CHANNEL, + BLUE_CHANNEL, + ALPHA_CHANNEL, + RGB_CHANNEL, + RGBA_CHANNEL +}; + + + +class ShaderStorageBufferCallback : public osg::StateAttributeCallback +{ +public: + void operator() (osg::StateAttribute* attr, osg::NodeVisitor* nv) + { + //if you need to process the data in your app-code , better leaving it on GPU and processing there, uploading per frame will make it slow +#if 0 + osg::ShaderStorageBufferBinding* ssbb = static_cast(attr); + osg::ShaderStorageBufferObject* ssbo + = static_cast(ssbb->getBufferObject()); + + osg::FloatArray* array = static_cast(ssbo->getBufferData(0)); + + float someValue = array->at(0); + //std::cout << "someValue now: " << someValue << std::endl; + //data transfer performance test + // array->dirty(); +#endif + } +}; + + +//do not forget to set OSG_FILE_PATH to default OSG-Data and make sure the new shaders are copied there under"shaders" +class ComputeNode : public osg::PositionAttitudeTransform +{ + +public: + + osg::ref_ptr _computeProgram; + osg::ref_ptr _computeShader; //compute and write position data in SSBO + + osg::ref_ptr _vertexShader; //reading position data from SSBO (OBS!: make sure glMemoryBuffer() is syncing this) + osg::ref_ptr _geometryShader; //building a quad looking to the camera + osg::ref_ptr _fragmentShader; //use false-colors etc. for making your data visible + + osg::ref_ptr _helperNode; // coordinate system node + + ref_ptr _ssbo; + ref_ptr _ssbb; + + GLfloat* _data; // some data we upload to GPU, initialised with random values + osg::ref_ptr _dataArray; // + + osg::ref_ptr _computationResultsRenderGroup; + osg::ref_ptr _computationResultsRenderProgram; + osg::ref_ptr _computationResultsRenderStateSet; + + + std::string _computeShaderSourcePath; + std::string _vertexShaderSourcePath; + std::string _geometryShaderSourcePath; + std::string _fragmentShaderSourcePath; + + + void addHelperGeometry(); + void addDataMonitor(osg::Vec3 placement, osg::Vec3 relativePlacement, float scale, Channel channel, BufferOffset shaderBufferOffset, std::string labelcaption, float minDataRange, float maxDataRange); + void addComputationResultsRenderTree(); + void initComputingSetup(); + + ComputeNode() + { + const char* envOsgFilePath = getenv("OSG_FILE_PATH"); + std::stringstream computeshaderpath; computeshaderpath << envOsgFilePath << "/shaders/osgssboComputeShader.cs"; + _computeShaderSourcePath = computeshaderpath.str(); + std::stringstream vertexshaderpath; vertexshaderpath << envOsgFilePath << "/shaders/osgssboVertexShader.vs"; + _vertexShaderSourcePath = vertexshaderpath.str(); + std::stringstream geometryshaderpath; geometryshaderpath << envOsgFilePath << "/shaders/osgssboGeometryShader.gs"; + _geometryShaderSourcePath = geometryshaderpath.str(); + std::stringstream fragmentshaderpath; fragmentshaderpath << envOsgFilePath << "/shaders/osgssboFragmentShader.fs"; + _fragmentShaderSourcePath = fragmentshaderpath.str(); + } + +}; + + +class ComputeNodeUpdateCallback : public osg::NodeCallback +{ +public: + + ComputeNode* _computeNode; + osg::Timer_t _prevShaderUpdateTime; + osg::Timer _timer; + + ComputeNodeUpdateCallback(){} + + ComputeNodeUpdateCallback(ComputeNode* computeNode) + { + _computeNode = computeNode; + _prevShaderUpdateTime = 0; + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osg::Timer_t currTime = _timer.tick(); + + if (_timer.delta_s(_prevShaderUpdateTime, currTime) > 1.0) //one second interval for shader-changed-do-reload check + { + osg::ref_ptr reloadedshader; + std::string runningSource; + std::string reloadedstring; + + if (_computeNode->_computeShader.valid()) + { + runningSource = _computeNode->_computeShader->getShaderSource(); + reloadedshader = osg::Shader::readShaderFile(osg::Shader::COMPUTE, _computeNode->_computeShaderSourcePath); + + reloadedstring = reloadedshader->getShaderSource(); + if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str())) + { + _computeNode->_computeProgram->removeShader(_computeNode->_computeShader.get()); + _computeNode->_computeShader = reloadedshader.get(); + _computeNode->_computeProgram->addShader(_computeNode->_computeShader.get()); + } + } + + if (_computeNode->_vertexShader.valid()) + { + + runningSource = _computeNode->_vertexShader->getShaderSource(); + reloadedshader = osg::Shader::readShaderFile(osg::Shader::VERTEX, _computeNode->_vertexShaderSourcePath); + + reloadedstring = reloadedshader->getShaderSource(); + if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str())) + { + _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_vertexShader.get()); + _computeNode->_vertexShader = reloadedshader.get(); + _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_vertexShader.get()); + } + } + + + + if (_computeNode->_geometryShader.valid()) + { + runningSource = _computeNode->_geometryShader->getShaderSource(); + reloadedshader = osg::Shader::readShaderFile(osg::Shader::GEOMETRY, _computeNode->_geometryShaderSourcePath); + + reloadedstring = reloadedshader->getShaderSource(); + if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str())) + { + _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_geometryShader.get()); + _computeNode->_geometryShader = reloadedshader.get(); + _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_geometryShader.get()); + } + } + + if (_computeNode->_fragmentShader.valid()) + { + runningSource = _computeNode->_fragmentShader->getShaderSource(); + reloadedshader = osg::Shader::readShaderFile(osg::Shader::FRAGMENT, _computeNode->_fragmentShaderSourcePath); + + reloadedstring = reloadedshader->getShaderSource(); + if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str())) + { + _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_fragmentShader.get()); + _computeNode->_fragmentShader = reloadedshader.get(); + _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_fragmentShader.get()); + } + } + + + _prevShaderUpdateTime = _timer.tick(); + } + + traverse(node, nv); + + } +}; + +//set OSG_FILE_PATH for loading axes.osgt +void ComputeNode::addHelperGeometry() +{ + _helperNode = osgDB::readNodeFile("axes.osgt"); + + if (_helperNode.valid()) + { + addChild(_helperNode.get()); + } + + //osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform; + //pat->setPosition(osg::Vec3(0.5, 0, 0.5)); + //osg::Geode *sphereGeode = new osg::Geode; + //float radius = 0.5f; + //osg::TessellationHints* hints = new osg::TessellationHints; + //hints->setDetailRatio(0.9f); + //osg::ShapeDrawable* sphere = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f, 0.0f), radius), hints); + //sphereGeode->addDrawable(sphere); + //sphere->setColor(osg::Vec4(0, 1, 0, 0.1)); + //osg::StateSet* stateset = sphereGeode->getOrCreateStateSet(); + //osg::BlendFunc *blend = new osg::BlendFunc; + //blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); + //stateset->setAttributeAndModes(blend, osg::StateAttribute::ON); + //pat->addChild(sphereGeode); + //addChild(pat); +} + + + + + + +void ComputeNode::addDataMonitor(osg::Vec3 placement, osg::Vec3 relativePlacement, float scale, Channel colorchannel, BufferOffset shaderStorageBufferOffset, std::string labelCaption, float minDataRange, float maxDataRange) +{ + osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform; + pat->setPosition(relativePlacement); + addChild(pat); + osg::Geometry* geom; + + if (NUM_ELEMENTS_X >= NUM_ELEMENTS_Y) + { + float ratio = (float)((float)NUM_ELEMENTS_Y / (float)NUM_ELEMENTS_X); + geom = osg::createTexturedQuadGeometry(placement, osg::Vec3(1.0f*scale, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, ratio*1.0f*scale)); + } + else + { + float ratio = (float)((float)NUM_ELEMENTS_X / (float)NUM_ELEMENTS_Y); + geom = osg::createTexturedQuadGeometry(placement, osg::Vec3(ratio*1.0f*scale, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, 1.0f*scale)); + + } + + geom->setVertexAttribArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); + + osg::ref_ptr quad = new osg::Geode; + quad->addDrawable(geom); + quad->setStateSet(getOrCreateStateSet()); + pat->addChild(quad.get()); + + static const char* vertexShaderSrcChannelMonitor = { + + "#version 430 \n" + + "uniform int numRows;\n" + "uniform int numCols;\n" + "uniform float osg_FrameTime;\n" + "uniform mat4 osg_ProjectionMatrix;\n" + "uniform mat4 osg_ModelViewMatrix;\n" + "out vec2 texCoordFromVertexShader;\n" + "struct particle{ float x; float y; float z; float w;};" + "layout (location = 0) in vec3 vertexpos;\n" + "attribute vec2 tex_coords;\n" + "void main() {\n" + "texCoordFromVertexShader.xy = tex_coords.xy; gl_Position = ( osg_ProjectionMatrix * osg_ModelViewMatrix * vec4(vertexpos.x,vertexpos.y,vertexpos.z,1) ); \n" + "}\n" + }; + + + + + std::stringstream fragmentshaderstringstreamChannelMonitor; + fragmentshaderstringstreamChannelMonitor << "#version 430\n"; + fragmentshaderstringstreamChannelMonitor << "uniform int numRows;\n"; + fragmentshaderstringstreamChannelMonitor << "uniform int numCols;\n"; + fragmentshaderstringstreamChannelMonitor << "uniform float dataRangeMin;\n"; + fragmentshaderstringstreamChannelMonitor << "uniform float dataRangeMax;\n"; + fragmentshaderstringstreamChannelMonitor << "in vec2 texCoordFromVertexShader;\n"; + fragmentshaderstringstreamChannelMonitor << "struct particle{ float x; float y; float z; float w;};"; + fragmentshaderstringstreamChannelMonitor << "layout(std140, binding=0) coherent buffer particles{particle p[];}; "; + fragmentshaderstringstreamChannelMonitor << "\n"; + fragmentshaderstringstreamChannelMonitor << "void main(void)\n"; + fragmentshaderstringstreamChannelMonitor << "{\n"; + fragmentshaderstringstreamChannelMonitor << "ivec2 storePos = ivec2(numRows*texCoordFromVertexShader.x, numCols*texCoordFromVertexShader.y); particle particleData = p[" << shaderStorageBufferOffset * NUM_ELEMENTS_X*NUM_ELEMENTS_Y << " + (storePos.x*numRows + storePos.y)]; "; + + //fragmentshaderstringstreamChannelMonitor << " memoryBarrierBuffer(); \n"; + fragmentshaderstringstreamChannelMonitor << " float dataRangeMultiplier = 1.0 / abs(dataRangeMax - dataRangeMin); \n"; + + switch (colorchannel) + { + case RED_CHANNEL: + { + fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.x; color.y =0.0; color.z = 0.0; color.w = 1.0; gl_FragColor = color;\n"; + + break; + } + case GREEN_CHANNEL: + { + fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.0; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.0; color.w = 1.0; gl_FragColor = color;\n"; + break; + } + case BLUE_CHANNEL: + { + fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.0; color.y = 0.0; color.z = 0.5+dataRangeMultiplier*particleData.z; color.w = 0.0 ; gl_FragColor = color;\n"; + break; + } + case ALPHA_CHANNEL: + { + fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.w; color.y = 0.5+dataRangeMultiplier*particleData.w; color.z = 0.5+dataRangeMultiplier*particleData.w; color.w = 0.5+0.5*particleData.w; gl_FragColor = color;\n"; + break; + } + + + case RGB_CHANNEL: + { + fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.x; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.5+dataRangeMultiplier*particleData.z; color.w = 1.0; gl_FragColor = color;\n"; + break; + } + + } + + fragmentshaderstringstreamChannelMonitor << "}\n"; + + + + + osg::Program * program = new osg::Program; + program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSrcChannelMonitor)); + program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentshaderstringstreamChannelMonitor.str().c_str())); + program->addBindAttribLocation("tex_coords", 1); + + osg::StateSet* ss = geom->getOrCreateStateSet(); + ss->setAttributeAndModes(program, osg::StateAttribute::ON); + ss->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_X)); + ss->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_Y)); + + ss->addUniform(new osg::Uniform("dataRangeMin", (float)minDataRange)); + ss->addUniform(new osg::Uniform("dataRangeMax", (float)maxDataRange)); + + + ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + //add a label + osgText::Text* text = new osgText::Text; + osgText::Font* font = osgText::readFontFile("fonts/arial.ttf"); + text->setFont(font); + text->setColor(osg::Vec4(1, 1, 1, 1)); + text->setCharacterSize(0.1*scale); + text->setPosition(placement + osg::Vec3(0.05, 0.05, 0)); + pat->setName(labelCaption); + text->setText(pat->getName()); + text->setBackdropType(osgText::Text::OUTLINE); + text->setBackdropImplementation(osgText::Text::POLYGON_OFFSET); + text->setBackdropOffset(0.05f); + text->setBackdropColor(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + quad->addDrawable(text); + + pat->addChild(quad.get()); + +} + +//compute texture image , taken from osgspotlight +osg::Image* createSpotLightImage(const osg::Vec4& centerColour, const osg::Vec4& backgroudColour, unsigned int size, float power) +{ + osg::Image* image = new osg::Image; + image->allocateImage(size, size, 1, + GL_RGBA, GL_UNSIGNED_BYTE); + + + float mid = (float(size) - 1)*0.5f; + float div = 2.0f / float(size); + for (unsigned int r = 0; r < size; ++r) + { + unsigned char* ptr = image->data(0, r, 0); + for (unsigned int c = 0; c < size; ++c) + { + float dx = (float(c) - mid)*div; + float dy = (float(r) - mid)*div; + float r = powf(1.0f - sqrtf(dx*dx + dy*dy), power); + if (r < 0.0f) r = 0.0f; + osg::Vec4 color = centerColour*r + backgroudColour*(1.0f - r); + *ptr++ = (unsigned char)((color[0])*255.0f); + *ptr++ = (unsigned char)((color[1])*255.0f); + *ptr++ = (unsigned char)((color[2])*255.0f); + *ptr++ = (unsigned char)((color[3])*255.0f); + } + } + return image; +} + + +void ComputeNode::addComputationResultsRenderTree() +{ + + _computationResultsRenderProgram = new osg::Program; + + _vertexShader = osg::Shader::readShaderFile(osg::Shader::VERTEX, _vertexShaderSourcePath); + _computationResultsRenderProgram->addShader(_vertexShader.get()); + + _geometryShader = osg::Shader::readShaderFile(osg::Shader::GEOMETRY, _geometryShaderSourcePath); + _computationResultsRenderProgram->addShader(_geometryShader.get()); + + _fragmentShader = osg::Shader::readShaderFile(osg::Shader::FRAGMENT, _fragmentShaderSourcePath); + _computationResultsRenderProgram->addShader(_fragmentShader.get()); + + + _computationResultsRenderProgram->addBindAttribLocation("tex_coords", 1); + + _computationResultsRenderGroup = new osg::Group; + _computationResultsRenderGroup->setDataVariance(osg::Object::DYNAMIC); + _computationResultsRenderStateSet = _computationResultsRenderGroup->getOrCreateStateSet(); + _computationResultsRenderStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + + osg::PointSprite *sprite = new osg::PointSprite; + int texture_unit = 0; + _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, sprite, osg::StateAttribute::ON); + _computationResultsRenderStateSet->setAttributeAndModes(_computationResultsRenderProgram, osg::StateAttribute::ON); + _computationResultsRenderStateSet->addUniform(new osg::Uniform("particleTexture", texture_unit)); + _computationResultsRenderStateSet->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_X)); + _computationResultsRenderStateSet->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_Y)); + + + _computationResultsRenderStateSet->setMode(GL_POINT_SMOOTH, osg::StateAttribute::ON); + _computationResultsRenderStateSet->setMode(GL_VERTEX_PROGRAM_POINT_SIZE_ARB, osg::StateAttribute::ON); + _computationResultsRenderStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON); + _computationResultsRenderStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + osg::Texture2D *tex = new osg::Texture2D(); + + osg::Image* particleImage = createSpotLightImage(osg::Vec4(1, 0, 0, 1), osg::Vec4(0.5, 0, 0, 0.0), 32, 0.7); + if (particleImage) + { + tex->setImage(particleImage); + } + _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, tex, osg::StateAttribute::ON); + + + osg::BlendFunc *blend = new osg::BlendFunc; + if (false) //emissive particles + { + blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE); + } + else + { + blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA); + } + + _computationResultsRenderStateSet->setAttributeAndModes(blend, osg::StateAttribute::ON); + + + osg::Depth* depth = new osg::Depth; + depth->setRange(0.0f, 0.0f); + depth->setFunction(osg::Depth::ALWAYS); + depth->setWriteMask(false); + depth->setFunction(osg::Depth::ALWAYS); + + _computationResultsRenderStateSet->setAttributeAndModes(depth, osg::StateAttribute::OFF); + + + osg::Geode* particleGeode = new osg::Geode; + unsigned int numVertices = NUM_ELEMENTS_X*NUM_ELEMENTS_Y; + + osg::Geometry* particleGeometry = new osg::Geometry; + particleGeometry->setUseDisplayList(false); + particleGeometry->setUseVertexBufferObjects(true); + + osg::Vec3Array* vertexarray = new osg::Vec3Array; + osg::Vec2Array* tcoords = new osg::Vec2Array; + + osg::Vec2 bottom_texcoord(0.0f, 0.0f); + + osg::Vec2 dx_texcoord(1.0f / (float)(NUM_ELEMENTS_X), 0.0f); + osg::Vec2 dy_texcoord(0.0f, 1.0f / (float)(NUM_ELEMENTS_Y)); + + + + for (int i = 0; i < NUM_ELEMENTS_X; i++) + { + osg::Vec2 texcoord = bottom_texcoord + dy_texcoord*(float)i; + + for (int j = 0; j < NUM_ELEMENTS_Y; j++) + { + vertexarray->push_back(osg::Vec3(texcoord.x(), texcoord.y(), 0.0)); + tcoords->push_back(osg::Vec2(texcoord.x(), texcoord.y())); + texcoord += dx_texcoord; + } + } + + particleGeometry->setVertexArray(vertexarray); + particleGeometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, numVertices)); + particleGeometry->setTexCoordArray(0, tcoords); + //this glMemoryBarrier thing... not sure if we could better do instanced drawing? all the data is in Shader Storage Buffer.. + particleGeometry->setVertexAttribArray(1, particleGeometry->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); + + _computationResultsRenderGroup->addChild(particleGeode); + particleGeode->addDrawable(particleGeometry); + + addChild(_computationResultsRenderGroup); + +} + + +void ComputeNode::initComputingSetup() +{ + + _computeProgram = new osg::Program; + _computeProgram->setComputeGroups((NUM_ELEMENTS_X / WORK_GROUP_SIZE) <= 1 ? 1 : (NUM_ELEMENTS_X / WORK_GROUP_SIZE), (NUM_ELEMENTS_Y / WORK_GROUP_SIZE) <= 1 ? 1 : (NUM_ELEMENTS_Y / WORK_GROUP_SIZE), 1); + _computeShader = osg::Shader::readShaderFile(osg::Shader::COMPUTE, _computeShaderSourcePath); + _computeProgram->addShader(_computeShader.get()); + + setDataVariance(osg::Object::DYNAMIC); + osg::StateSet* statesetComputation = getOrCreateStateSet(); + statesetComputation->setAttributeAndModes(_computeProgram.get()); + statesetComputation->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_X)); + statesetComputation->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_Y)); + statesetComputation->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + //blocksize + int numParticles = NUM_ELEMENTS_X * NUM_ELEMENTS_Y; + const unsigned blockSize = numParticles * __numChannels * __numDataValuesPerChannel* sizeof(GLfloat); + + //init all the particle data array + int idx = 0; + _data = new GLfloat[NUM_ELEMENTS_X * NUM_ELEMENTS_Y * __numChannels * __numDataValuesPerChannel]; + _dataArray = new FloatArray; + + //init the data array somehow, this way all is stored in one BufferObject. maybe better using multiple buffers instead? not sure what is faster and better for threading + for (int d = 0; d < __numDataValuesPerChannel; ++d) + { + for (int i = 0; i < NUM_ELEMENTS_X; ++i) + { + + for (int j = 0; j < NUM_ELEMENTS_Y; ++j) + { + + for (int k = 0; k < __numChannels; ++k) + { + switch (k) + { + + case (RED_CHANNEL) : + { + if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position + { + *_data = random(0.25, 0.75); + } + if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity + { + *_data = random(-2.4, 2.4); + } + if (d == ACCELERATION_OFFSET) //acceleration + { + *_data = random(-3.0, 3.0); + } + + if (d == PROPERTIES_OFFSET) //property particle mass (compute shader is computing sphere mass from radius instead) + { + *_data = random(0.2, 15.0); + } + + break; + } + + case (GREEN_CHANNEL) : + { + if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position + { + *_data = random(0.25, 0.75); + } + if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity + { + *_data = random(-2.4, 2.4); + } + + if (d == ACCELERATION_OFFSET)//acceleration + { + *_data = random(-3.0, 3.0); + } + if (d == PROPERTIES_OFFSET) //property particle radius + { + *_data = random(0.07, 0.219); + } + + break; + } + + case (BLUE_CHANNEL) : + { + if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position + { + *_data = random(0.25, 0.75); + } + if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity + { + *_data = random(-2.4, 2.4); + } + + if (d == ACCELERATION_OFFSET)//acceleration + { + *_data = random(-3.0, 3.0); + } + + + if (d == PROPERTIES_OFFSET) //place for some other property + { + *_data = random(0.0, 0.0); + } + + break; + } + + case (ALPHA_CHANNEL) : + { + if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position + { + *_data = random(1.0, 1.0); + } + if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity + { + *_data = random(-2.4, 2.4); + } + + if (d == ACCELERATION_OFFSET) //acceleration + { + //*_data = random(1.0, 1.0); + *_data = random(0.0, 0.0); + } + + if (d == PROPERTIES_OFFSET) //place for some other property + { + *_data = random(0.3, 0.3); + } + + break; + } + + + + } + _dataArray->push_back(*_data); + _data++; + idx++; + } + } + } + } + + _ssbo = new osg::ShaderStorageBufferObject; + _dataArray->setBufferObject(_ssbo.get()); + + + _ssbb = new osg::ShaderStorageBufferBinding(0, _ssbo.get(), 0, blockSize); + statesetComputation->setAttributeAndModes(_ssbb.get(), osg::StateAttribute::ON); + + + //option, do something useful with data or test the transfer speed + //_ssbb->setUpdateCallback(new ShaderStorageBufferCallback); + + //adding a quad , visualizing data in buffer + addDataMonitor(osg::Vec3(0, -1, 0), osg::Vec3(SUB_PLACEMENT_OFFSET_HORIZONTAL * 0, -SUB_PLACEMENT_OFFSET_VERTICAL * -2.0, SUB_PLACEMENT_OFFSET_HORIZONTAL * 0), 1.0, RGB_CHANNEL, POSITION_NOW_OFFSET, "X,Y,Z - PositionNow", -1.0, 1.0); + + //the coord from default dataset + addHelperGeometry(); + + + addComputationResultsRenderTree(); + +} + + +//taken from osgdistorsion example for getting it nice on screen with antialiasing +osg::Node* createPrerenderSubgraph(osg::Node* subgraph, const osg::Vec4& clearColour) +{ + osg::Group* prerenderNode = new osg::Group; + + unsigned int tex_width = PRERENDER_WIDTH; + unsigned int tex_height = PRERENDER_HEIGHT; + + osg::Texture2D* texture = new osg::Texture2D; + texture->setTextureSize(tex_width, tex_height); + texture->setInternalFormat(GL_RGBA); + texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); + texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); + + { + osg::Camera* prerenderCamera = new osg::Camera; + prerenderCamera->setClearColor(clearColour); + prerenderCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + prerenderCamera->setReferenceFrame(osg::Transform::RELATIVE_RF); + prerenderCamera->setProjectionMatrix(osg::Matrixd::identity()); + prerenderCamera->setViewMatrix(osg::Matrixd::identity()); + prerenderCamera->setViewport(0, 0, tex_width, tex_height); + prerenderCamera->setRenderOrder(osg::Camera::PRE_RENDER); + prerenderCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + prerenderCamera->attach(osg::Camera::COLOR_BUFFER0, texture, 0, 0, false, PRERENDER_ANTIALIASINGMULTISAMPLES, PRERENDER_ANTIALIASINGMULTISAMPLES); + prerenderCamera->addChild(subgraph); + prerenderNode->addChild(prerenderCamera); + + } + + { + osg::Geometry* polyGeom = new osg::Geometry(); + + polyGeom->setSupportsDisplayList(false); + + osg::Vec3 origin(0.0f, 0.0f, 0.0f); + osg::Vec3 xAxis(1.0f, 0.0f, 0.0f); + osg::Vec3 yAxis(0.0f, 1.0f, 0.0f); + + float height = 1024.0f; + float width = 1280.0f; + int noSteps = 3; + + osg::Vec3Array* vertices = new osg::Vec3Array; + osg::Vec2Array* texcoords = new osg::Vec2Array; + osg::Vec4Array* colors = new osg::Vec4Array; + + osg::Vec3 bottom = origin; + osg::Vec3 dx = xAxis*(width / ((float)(noSteps - 1))); + osg::Vec3 dy = yAxis*(height / ((float)(noSteps - 1))); + + osg::Vec2 bottom_texcoord(0.0f, 0.0f); + osg::Vec2 dx_texcoord(1.0f / (float)(noSteps - 1), 0.0f); + osg::Vec2 dy_texcoord(0.0f, 1.0f / (float)(noSteps - 1)); + + int i, j; + for (i = 0; i < noSteps; ++i) + { + osg::Vec3 cursor = bottom + dy*(float)i; + osg::Vec2 texcoord = bottom_texcoord + dy_texcoord*(float)i; + for (j = 0; j < noSteps; ++j) + { + vertices->push_back(cursor); + texcoords->push_back(osg::Vec2((sin(texcoord.x()*osg::PI - osg::PI*0.5) + 1.0f)*0.5f, (sin(texcoord.y()*osg::PI - osg::PI*0.5) + 1.0f)*0.5f)); + colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); + cursor += dx; + texcoord += dx_texcoord; + } + } + + polyGeom->setVertexArray(vertices); + polyGeom->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + polyGeom->setTexCoordArray(0, texcoords); + + for (i = 0; i < noSteps - 1; ++i) + { + osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::QUAD_STRIP); + for (j = 0; j < noSteps; ++j) + { + elements->push_back(j + (i + 1)*noSteps); + elements->push_back(j + (i)*noSteps); + } + polyGeom->addPrimitiveSet(elements); + } + + osg::StateSet* stateset = polyGeom->getOrCreateStateSet(); + stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON); + stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + osg::Geode* geode = new osg::Geode(); + geode->addDrawable(polyGeom); + + osg::Camera* nestedRenderCamera = new osg::Camera; + nestedRenderCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + nestedRenderCamera->setViewMatrix(osg::Matrix::identity()); + nestedRenderCamera->setProjectionMatrixAsOrtho2D(0, 1280, 0, 1024); + nestedRenderCamera->setRenderOrder(osg::Camera::NESTED_RENDER); + nestedRenderCamera->addChild(geode); + + prerenderNode->addChild(nestedRenderCamera); + } + + return prerenderNode; +} + + + + +int main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc, argv); + + osgViewer::Viewer viewer; + + osg::ref_ptr scene = new osg::Group; + + + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + viewer.addEventHandler(new osgViewer::StatsHandler); + viewer.addEventHandler(new osgViewer::WindowSizeHandler); + viewer.addEventHandler(new osgViewer::ThreadingHandler); + viewer.getCamera()->setProjectionMatrixAsPerspective(60.0f, 1.33333, 0.01, 100.0); + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); + + viewer.setUpViewInWindow(11, 11, 800 + 11, 600 + 11); + //viewer.setUpViewOnSingleScreen(0); // !! + + viewer.getCamera()->setClearColor(osg::Vec4(0.3, 0.3, 0.3, 1.0)); + viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);// we can play with threading models later + + osg::ref_ptr computeNode = new ComputeNode(); + computeNode->setPosition(osg::Vec3(0, 0, 0)); + computeNode->setUpdateCallback(new ComputeNodeUpdateCallback(computeNode)); // on-the-fly reloading the shaders if shader source on disk is changed + computeNode->initComputingSetup(); + + + + scene->addChild(computeNode.get()); + scene->addChild(computeNode->_computationResultsRenderGroup.get()); + + +#ifdef PRERENDER_HIGH_QUALITY_ANTIALIASING + viewer.setSceneData(createPrerenderSubgraph(scene.get(), osg::Vec4(0.3, 0.4, 0.6, 1))); +#else + viewer.setSceneData(scene.get()); +#endif + + viewer.realize(); + + viewer.getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true); + + viewer.run(); + + return 1; +} diff --git a/include/osg/BufferIndexBinding b/include/osg/BufferIndexBinding index 83a459490..ee58ed7af 100644 --- a/include/osg/BufferIndexBinding +++ b/include/osg/BufferIndexBinding @@ -164,6 +164,35 @@ class OSG_EXPORT AtomicCounterBufferBinding : public BufferIndexBinding } }; +class OSG_EXPORT ShaderStorageBufferBinding : public BufferIndexBinding +{ + public: + ShaderStorageBufferBinding(GLuint index=0); + /** Create a binding for a shader storage buffer index target. + * @param index the index target + * @param bo associated buffer object + * @param offset offset into buffer object + * @param size size of data in buffer object + */ + ShaderStorageBufferBinding(GLuint index, BufferObject* bo, GLintptr offset, GLsizeiptr size); + ShaderStorageBufferBinding(const ShaderStorageBufferBinding& rhs, const CopyOp& copyop=CopyOp::SHALLOW_COPY); + META_StateAttribute(osg, ShaderStorageBufferBinding, SHADERSTORAGEBUFFERBINDING); + + virtual int compare(const StateAttribute& bb) const + { + COMPARE_StateAttribute_Types(ShaderStorageBufferBinding, bb) + COMPARE_StateAttribute_Parameter(_target) + COMPARE_StateAttribute_Parameter(_index) + COMPARE_StateAttribute_Parameter(_bufferObject) + COMPARE_StateAttribute_Parameter(_offset) + COMPARE_StateAttribute_Parameter(_size) + return 0; + } +}; + + + + } // namespace osg #endif diff --git a/include/osg/BufferObject b/include/osg/BufferObject index 1281c7301..bc1cfb36b 100644 --- a/include/osg/BufferObject +++ b/include/osg/BufferObject @@ -732,6 +732,7 @@ class OSG_EXPORT PixelDataBufferObject : public BufferObject }; + class OSG_EXPORT UniformBufferObject : public BufferObject { public: @@ -759,6 +760,20 @@ inline void GLBufferObject::bindBuffer() if (_set) _set->moveToBack(this); } + +class OSG_EXPORT ShaderStorageBufferObject : public BufferObject +{ + public: + + ShaderStorageBufferObject(); + ShaderStorageBufferObject(const ShaderStorageBufferObject& ubo, const CopyOp& copyop=CopyOp::SHALLOW_COPY); + META_Object(osg, ShaderStorageBufferObject); + protected: + virtual ~ShaderStorageBufferObject(); +}; + + + } #endif diff --git a/include/osg/StateAttribute b/include/osg/StateAttribute index 228db3658..c4a221a04 100644 --- a/include/osg/StateAttribute +++ b/include/osg/StateAttribute @@ -199,6 +199,8 @@ class OSG_EXPORT StateAttribute : public Object VERTEX_ATTRIB_DIVISOR, + SHADERSTORAGEBUFFERBINDING, + CAPABILITY = 100 }; diff --git a/src/osg/BufferIndexBinding.cpp b/src/osg/BufferIndexBinding.cpp index 4ec16e2ec..35c030f38 100644 --- a/src/osg/BufferIndexBinding.cpp +++ b/src/osg/BufferIndexBinding.cpp @@ -143,4 +143,26 @@ void AtomicCounterBufferBinding::readData(osg::State & state, osg::UIntArray & u bo->_extensions->glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, static_cast(previousID)); } + +ShaderStorageBufferBinding::ShaderStorageBufferBinding(GLuint index) + : BufferIndexBinding(GL_SHADER_STORAGE_BUFFER, index) +{ +} + +ShaderStorageBufferBinding::ShaderStorageBufferBinding(GLuint index, BufferObject* bo, GLintptr offset, GLsizeiptr size) + : BufferIndexBinding(GL_SHADER_STORAGE_BUFFER, index, bo, offset, size) +{ + +} + +ShaderStorageBufferBinding::ShaderStorageBufferBinding(const ShaderStorageBufferBinding& rhs, const CopyOp& copyop) + : BufferIndexBinding(rhs, copyop) +{ +} + + + + + + } // namespace osg diff --git a/src/osg/BufferObject.cpp b/src/osg/BufferObject.cpp index 086d63ad1..dd1ccb856 100644 --- a/src/osg/BufferObject.cpp +++ b/src/osg/BufferObject.cpp @@ -1560,3 +1560,25 @@ AtomicCounterBufferObject::AtomicCounterBufferObject(const AtomicCounterBufferOb AtomicCounterBufferObject::~AtomicCounterBufferObject() { } + + +////////////////////////////////////////////////////////////////////////////////// +// +// ShaderStorageBufferObject +// +ShaderStorageBufferObject::ShaderStorageBufferObject() +{ + setTarget(GL_SHADER_STORAGE_BUFFER); + setUsage(GL_STATIC_DRAW); +} + +ShaderStorageBufferObject::ShaderStorageBufferObject(const ShaderStorageBufferObject& ubo, const CopyOp& copyop) + : BufferObject(ubo, copyop) +{ +} + +ShaderStorageBufferObject::~ShaderStorageBufferObject() +{ +} + +