From 1beddd3d29424347a2f2688b80ac4cf79c98dab0 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 23 Mar 2017 19:36:01 +0000 Subject: [PATCH] Fixed file endings --- examples/osgbindlesstext/osgbindlesstext.cpp | 1192 +++++++++--------- 1 file changed, 596 insertions(+), 596 deletions(-) diff --git a/examples/osgbindlesstext/osgbindlesstext.cpp b/examples/osgbindlesstext/osgbindlesstext.cpp index aae24f6db..330784b66 100644 --- a/examples/osgbindlesstext/osgbindlesstext.cpp +++ b/examples/osgbindlesstext/osgbindlesstext.cpp @@ -1,596 +1,596 @@ -/* OpenSceneGraph example, osgbindlesstex. -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -* THE SOFTWARE. -*/ - -///\author David A Heitbrink -/// This is an example implementation of the use of bindless textures. -/// "bindless" textures are relatively simple concept, basically -/// you get a texture handle, then ask the driver to keep said -/// handle resident. -/// -/// Once the texture has been made resident, we need to upload -/// the handle (a 64 bit unsigned int) to the shader. This can -/// be done in a number of ways, through attributes, uniform -/// buffer objects, shader buffer objects or just plain uniforms. -/// -/// The basic point of the bindless texture is to remove the need -/// to bind a new texture every time we want to render something -/// with a different texture. Generally speaking in broad terms -/// driver overhead tends to be a huge bottle neck on modern -/// hardware (as of late 2016). By using bindless textures -/// we can remove alot of calls to the driver to switch active -/// textures while rendering. What this also allows us to do -/// is to consolidate more objects + draw states as we do -/// not need to change textures, this save us alot of calls to -/// the driver. -/// -/// This example combines instancing with bindless textures -/// to draw 1000 cubes, each with a unique texture. This is -/// a pretty simplified example, where each instance ID is -/// used as a index into the array of textures. -/// -/// One of the powerfull things about bindless textures is it allows -/// many more objects to be combined into a single drawable. -/// However to do this you may need to add an attribute to -/// use an index into the array of texture handles, and not -/// just use the instance ID like in this example. - -#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 -//Hard coded constant for number unique textures -const int TextureCount = 1000; -// To use bindless textures, we need to tell the GPU to -// enable the use of 64 bit integers and bindless textures -// -// -// At this time (late 2016) NVidia drivers seem to dislike -// the ARB, GL_ARB_bindless_texture, if you do not have a -// NVidia driver, you will most likely have to change: -// GL_NV_gpu_shader5 -// to: -// GL_ARB_gpu_shader5 - -//the XXX is so we can replace it with a string from TextureCount -std::string vertShader= -"#version 450 compatibility \n" -"#extension GL_ARB_bindless_texture : require \n" -"#extension GL_NV_gpu_shader5 : require // uint64_t \n" -"//#extension GL_ARB_gpu_shader5 : require // uint64_t \n" -"//#extension GL_ARB_gpu_shader_int64: require // uint64_t \n" -"in float osg_FrameTime; \n" -"out vec2 TexCoord; \n" -"flat out int textureIndex; \n" -"void main() { \n" -" mat4 scale =mat4(0.01, 0.00, 0.00, 0.00, \n" -" 0.00, 0.01, 0.00, 0.00, \n" -" 0.00, 0.00, 0.01, 0.00, \n" -" 0.00, 0.00, 0.00, 1.00); \n" -" vec4 pos = gl_Vertex*scale; \n" -" pos.x += ((gl_InstanceID%100)/10)*0.015-0.075; \n" -" pos.y += (gl_InstanceID/100)*0.015 - 0.075; \n" -" pos.z += (gl_InstanceID%10)*0.015 - 0.075; \n" -" pos.w = 1; \n" -" gl_Position = gl_ModelViewProjectionMatrix*pos; \n" -" TexCoord = gl_MultiTexCoord0.xy; \n" -" textureIndex = gl_InstanceID%XXX; \n" -"} \n" -; -//we could setup tex to be of type sampler2D, and not have to do -//the type conversion, but I wanted to added code to test if tex -//had a value set. -//If we get a red cube, we are not getting our handle from our UBO, -//if we get a black cube, then we are having an issue with the -//texture handle itself -std::string fragShader = -"#version 450 compatibility \n" -"#extension GL_ARB_bindless_texture : require \n" -"#extension GL_NV_gpu_shader5 : require // uint64_t \n" -"//#extension GL_ARB_gpu_shader5 : require // uint64_t \n" -"//#extension GL_ARB_gpu_shader_int64: require // uint64_t \n" -"uniform sampler2D TextureId; \n" -"in vec2 TexCoord; \n" -"flat in int textureIndex; \n" -"layout (binding = 0, std140) uniform TEXTURE_BLOCK \n" -"{ \n" -" uint64_t tex[XXX]; \n" -"}; \n" -"void main() { \n" -" int tIndex = (int)(textureIndex); \n" -" sampler2D myText = sampler2D(tex[tIndex]); \n" -" gl_FragColor = texture2D(myText,TexCoord); \n" -" if (tex[tIndex] == 0) gl_FragColor.r = 1.0; \n" -"} \n" -; - -///This class provides a basic wraper for a Uniform Buffer Object -///or UBO, and provides the storage for the texture handles -class BindlessBuffer: public osg::Referenced{ -public: - typedef osg::ref_ptr UniBufferObjRef; - typedef osg::ref_ptr UniBufferBindingRef; - typedef osg::ref_ptr HandleArrayRef; - typedef osg::ref_ptr BindlessBufferRef; - static BindlessBufferRef Make(size_t count){ - BindlessBufferRef val = new BindlessBuffer(); - val->_count = count; - val->_sbbo = new osg::UniformBufferObject; - val->_handles = new osg::UInt64Array(); - val->_handles->resize(count*2,0); - val->_handles->setBufferObject(val->_sbbo); - val->_ssbb = new osg::UniformBufferBinding(0, val->_sbbo.get(), 0, sizeof(GLuint64)*count); - return val; - } - BindlessBuffer& operator = (const BindlessBuffer& rhs){ - if (this != &rhs){ - _count=rhs._count; - _sbbo =rhs._sbbo ; - _ssbb =rhs._ssbb ; - _handles = rhs._handles; - } - return *this; - } - BindlessBuffer(const BindlessBuffer& rhs):osg::Referenced(rhs){ - if (this != &rhs){ - _count=rhs._count; - _sbbo =rhs._sbbo ; - _ssbb =rhs._ssbb ; - _handles = rhs._handles; - } - } - UniBufferObjRef& Object(){return _sbbo;} - UniBufferBindingRef& Binding(){return _ssbb;} - HandleArrayRef& Handles(){return _handles;} - int count(){return _count;} -private: - int _count; - UniBufferObjRef _sbbo; - UniBufferBindingRef _ssbb; - HandleArrayRef _handles; - - BindlessBuffer():osg::Referenced(),_count(0){ - } -}; - -///This class extends a Texture, when this is texture is applied -///the first time, it will setup all our texture handles, after that -///it will not make any more GL calls until it gets released -class BindlessTexture: public osg::Texture2D -{ -public: - typedef osg::ref_ptr BufferRef; - typedef std::vector > TextureList; - typedef std::vector HandleList; - typedef osg::ref_ptr< osg::Texture::TextureObject> TextureObjectRef; - typedef std::vector TextureObjectList; - typedef osg::buffered_object TextureObjectBuffer; - - BindlessTexture(); - BindlessTexture(BufferRef, TextureList); - BindlessTexture(const BindlessTexture& rhs, const osg::CopyOp& copy =osg::CopyOp::SHALLOW_COPY); - void releaseGLObjects(osg::State* state) const; - void resizeGLObjectBuffers(unsigned maxSize); - void setBidlessIndex(unsigned int index); - META_StateAttribute(osg, BindlessTexture, TEXTURE); - - void apply(osg::State& state) const; -protected: - void applyOnce(osg::State &state) const; - mutable osg::buffered_object _handles; - mutable TextureList _textureList; - mutable osg::ref_ptr _buffer; - mutable std::vector _isBound; - mutable TextureObjectBuffer _textureBufferList; - // array index = texture image unit. - unsigned int _bindlessIndex; -}; - - -BindlessTexture::BindlessTexture():osg::Texture2D(),_bindlessIndex(0) -{ - _isBound.resize(5,false); -} - -BindlessTexture::BindlessTexture(const BindlessTexture& rhs, const osg::CopyOp& copy) - :osg::Texture2D( rhs, copy ) -{ - _isBound.resize(5,false); - _buffer = rhs._buffer; - _bindlessIndex = rhs._bindlessIndex; - for(unsigned i=0; i image = _image; - if (_handles[contextID].size() < _textureList.size()) - _handles[contextID].resize( _textureList.size(),0); - if (_textureBufferList[contextID].size() < _textureList.size()) - _textureBufferList[contextID].resize( _textureList.size()); - int txtcount = _textureList.size(); - if (_buffer->count() < txtcount) - txtcount = _buffer->count(); - //for each actual texture we have, bind it, get the texture hande, assign the value to our UBO - for (int i = 0; i bind(); - applyTexParameters(GL_TEXTURE_2D,state); - - applyTexImage2D_load(state,GL_TEXTURE_2D,image.get(), - _textureWidth, _textureHeight, _numMipmapLevels); - textureObject->setAllocated(true); - _textureBufferList[contextID][i] = textureObject; - - //Here is where the "magic" happens, we get the texture handle for our texture, copy it to our UBO, - //and then tell OpenGL to keep the handle resident - _handles[contextID][i] = extensions->glGetTextureHandle( textureObject->id() ); - std::vector &vec = _buffer->Handles()->asVector(); - vec[i*2] = _handles[contextID][i]; - _buffer->Object()->dirty(); - _buffer->Handles()->dirty(); - - if ( _handles[contextID][i] != 0L || extensions->glIsTextureHandleResident( _handles[contextID][i]) == GL_FALSE) - { - extensions->glMakeTextureHandleResident( _handles[contextID][i] ); - } - } - - // update the modified tag to show that it is up to date. - getModifiedCount(contextID) = image->getModifiedCount(); -} - -void BindlessTexture::apply(osg::State& state) const -{ - unsigned contextID = state.getContextID(); - if ( _isBound[contextID] == false ) - { - applyOnce(state); - _isBound[contextID] = true; - }else{ - //we should mostly hit this durring the lifetime of this object, - //note we basically do nothing...... - } -} -/// cleanup, we just need to tell OpenGL to release our texture handle -void BindlessTexture::releaseGLObjects(osg::State* state) const -{ - if ( state ) - { - unsigned contextID = state->getContextID(); - osg::Texture2D::releaseGLObjects( state ); - osg::GLExtensions* ext = osg::GLExtensions::Get( contextID, true ); - - for(unsigned i=0; i<_handles[contextID].size(); ++i) - { - ext->glMakeTextureHandleNonResident( _handles[contextID][i] ); - _handles[contextID][i] = 0; - } - - } -} - -void -BindlessTexture::resizeGLObjectBuffers(unsigned maxSize) -{ - osg::Texture2D::resizeGLObjectBuffers( maxSize ); - - size_t handleSize = _handles.size(); - size_t txtSize = _textureList.size(); - size_t boundSize = _isBound.size(); - if ( handleSize < maxSize ) { - _isBound.resize(maxSize,false); - } - if ( handleSize < maxSize ) { - _handles.resize( maxSize ); - for(unsigned i=handleSize; i<_handles.size(); ++i){ - for(unsigned j=0; j ImageRef; -/////////////////////////////////////////////////////// -///Create an array of images, with checkerboard -///pattern with random color and size -/// -void createImageArray(osg::StateSet* attachPnt){ - BindlessTexture::TextureList images; - images.resize(TextureCount); - BindlessBuffer::BindlessBufferRef buffer = BindlessBuffer::Make(TextureCount); - srand (time(NULL)); - for (int i =0; i < TextureCount; i++){ - ImageRef tImage = new osg::Image(); - int powerOf2 = rand()%6+4; - const unsigned int imageSize = 1<allocateImage(imageSize,imageSize,1,GL_RGBA,GL_UNSIGNED_BYTE); - unsigned char* buff = tImage->data(); - const int stride = 4; - unsigned char primaryColor[4]; - - int boxWidth = rand()%15+2; - int boxLength = rand()%15+2; - - //light squares - primaryColor[0] = rand()%128 + 128; - primaryColor[1] = rand()%128 + 128; - primaryColor[2] = rand()%128 + 128; - //dark squares - unsigned char secondaryColor[4]; - secondaryColor[0] = rand()%128; - secondaryColor[1] = rand()%128; - secondaryColor[2] = rand()%128; - for (int x = 0; x < imageSize; x++){ - for (int y =0; y0; - if (isPrimaryColor){ - pixel[0] = primaryColor[0]; - pixel[1] = primaryColor[1]; - pixel[2] = primaryColor[2]; - }else{ - pixel[0] = secondaryColor[0]; - pixel[1] = secondaryColor[1]; - pixel[2] = secondaryColor[2]; - } - pixel[3] = 255; - } - } - images[i] = tImage; - - std::stringstream sstr; - sstr<<"Image"<setName(sstr.str()); - } - BindlessTexture* tex = new BindlessTexture(buffer,images); - attachPnt->setTextureAttribute(0,tex,osg::StateAttribute::ON); - attachPnt->setAttributeAndModes(buffer->Binding(), osg::StateAttribute::ON); -} -///Create a cube centered at the origin, with given by size -/// -osg::Geometry* createCube(float scale, osg::Vec3 origin = osg::Vec3(0.0f,0.0f,0.0f) ) -{ - osg::Geometry* geometry = new osg::Geometry; - geometry->setName("TexturedCubeArray"); - - osg::Vec3Array* vertices = new osg::Vec3Array; - geometry->setVertexArray(vertices); - - osg::Vec2Array* tcoords = new osg::Vec2Array(); - geometry->setTexCoordArray(0,tcoords); - - origin -= osg::Vec3(scale/2.0f,scale/2.0f,scale/2.0f); - osg::Vec3 dx(scale,0.0f,0.0f); - osg::Vec3 dy(0.0f,scale,0.0f); - osg::Vec3 dz(0.0f,0.0f,scale); - - { - // front face - vertices->push_back(origin); - vertices->push_back(origin+dx); - vertices->push_back(origin+dx+dz); - vertices->push_back(origin+dz); - - tcoords->push_back(osg::Vec2(0.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,1.0f)); - tcoords->push_back(osg::Vec2(0.0f,1.0f)); - } - - { - // back face - vertices->push_back(origin+dy); - vertices->push_back(origin+dy+dz); - vertices->push_back(origin+dy+dx+dz); - vertices->push_back(origin+dy+dx); - - tcoords->push_back(osg::Vec2(0.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,1.0f)); - tcoords->push_back(osg::Vec2(0.0f,1.0f)); - } - - { - // left face - vertices->push_back(origin+dy); - vertices->push_back(origin); - vertices->push_back(origin+dz); - vertices->push_back(origin+dy+dz); - - tcoords->push_back(osg::Vec2(0.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,1.0f)); - tcoords->push_back(osg::Vec2(0.0f,1.0f)); - } - - { - // right face - vertices->push_back(origin+dx+dy); - vertices->push_back(origin+dx+dy+dz); - vertices->push_back(origin+dx+dz); - vertices->push_back(origin+dx); - - tcoords->push_back(osg::Vec2(0.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,1.0f)); - tcoords->push_back(osg::Vec2(0.0f,1.0f)); - } - - { - // top face - vertices->push_back(origin+dz); - vertices->push_back(origin+dz+dx); - vertices->push_back(origin+dz+dx+dy); - vertices->push_back(origin+dz+dy); - - tcoords->push_back(osg::Vec2(0.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,1.0f)); - tcoords->push_back(osg::Vec2(0.0f,1.0f)); - } - - { - // bottom face - vertices->push_back(origin); - vertices->push_back(origin+dy); - vertices->push_back(origin+dx+dy); - vertices->push_back(origin+dx); - - tcoords->push_back(osg::Vec2(0.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,0.0f)); - tcoords->push_back(osg::Vec2(1.0f,1.0f)); - tcoords->push_back(osg::Vec2(0.0f,1.0f)); - } - osg::DrawArrays* primSet = new osg::DrawArrays(GL_QUADS, 0, vertices->size()); - geometry->addPrimitiveSet(primSet); - - return geometry; -} -/// -///Here we are going to create our scene, basically we need to make sure our -///Bindless texture gets applied before our shader programs. -/// -/// -osg::Group* CreateScene(){ - osg::Group* sceneRoot= new osg::Group(); - sceneRoot->setName("Root"); - osg::Geode *geo = new osg::Geode(); - geo->setName("Geo"); - sceneRoot->addChild(geo); - float size = 1.0f; - osg::StateSet* scene_ss = sceneRoot->getOrCreateStateSet(); - createImageArray(scene_ss); - scene_ss->setMode(GL_DEPTH_TEST,osg::StateAttribute::ON); - - osg::ref_ptr geom = createCube(0.9f); - osg::PrimitiveSet *prim = geom->getPrimitiveSet(0); - //instanced elements must use VBOs - geom->setUseDisplayList(false); - geom->setUseVertexBufferObjects(true); - geom->setCullingActive(false); - prim->setNumInstances(1000); - prim->dirty(); - sceneRoot->addChild(geo); - geo->addDrawable(geom.get()); - - osg::StateSet* ss = geo->getOrCreateStateSet(); - - std::string strTextureCount; - std::stringstream ssconv; - ssconv<>strTextureCount; - std::string::size_type pos = vertShader.find("XXX"); - vertShader.replace(pos,size_t(3),strTextureCount); - - pos = fragShader.find("XXX"); - fragShader.replace(pos,size_t(3),strTextureCount); - - osg::Program* program = new osg::Program; - osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertShader); - program->addShader(vertex_shader); - - osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragShader); - program->addShader(fragment_shader); - - ss->setAttributeAndModes(program, osg::StateAttribute::ON); - - - return sceneRoot; - } -int main(int argc, char** argv) -{ - // set command line options - osg::ArgumentParser arguments(&argc,argv); - - // construct the viewer. - osgViewer::Viewer viewer(arguments); - - // add the stats handler - viewer.addEventHandler(new osgViewer::StatsHandler); - - // add model to viewer. - viewer.setSceneData( CreateScene() ); - - viewer.getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true); - - return viewer.run(); -} - +/* OpenSceneGraph example, osgbindlesstex. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +///\author David A Heitbrink +/// This is an example implementation of the use of bindless textures. +/// "bindless" textures are relatively simple concept, basically +/// you get a texture handle, then ask the driver to keep said +/// handle resident. +/// +/// Once the texture has been made resident, we need to upload +/// the handle (a 64 bit unsigned int) to the shader. This can +/// be done in a number of ways, through attributes, uniform +/// buffer objects, shader buffer objects or just plain uniforms. +/// +/// The basic point of the bindless texture is to remove the need +/// to bind a new texture every time we want to render something +/// with a different texture. Generally speaking in broad terms +/// driver overhead tends to be a huge bottle neck on modern +/// hardware (as of late 2016). By using bindless textures +/// we can remove alot of calls to the driver to switch active +/// textures while rendering. What this also allows us to do +/// is to consolidate more objects + draw states as we do +/// not need to change textures, this save us alot of calls to +/// the driver. +/// +/// This example combines instancing with bindless textures +/// to draw 1000 cubes, each with a unique texture. This is +/// a pretty simplified example, where each instance ID is +/// used as a index into the array of textures. +/// +/// One of the powerfull things about bindless textures is it allows +/// many more objects to be combined into a single drawable. +/// However to do this you may need to add an attribute to +/// use an index into the array of texture handles, and not +/// just use the instance ID like in this example. + +#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 +//Hard coded constant for number unique textures +const int TextureCount = 1000; +// To use bindless textures, we need to tell the GPU to +// enable the use of 64 bit integers and bindless textures +// +// +// At this time (late 2016) NVidia drivers seem to dislike +// the ARB, GL_ARB_bindless_texture, if you do not have a +// NVidia driver, you will most likely have to change: +// GL_NV_gpu_shader5 +// to: +// GL_ARB_gpu_shader5 + +//the XXX is so we can replace it with a string from TextureCount +std::string vertShader= +"#version 450 compatibility \n" +"#extension GL_ARB_bindless_texture : require \n" +"#extension GL_NV_gpu_shader5 : require // uint64_t \n" +"//#extension GL_ARB_gpu_shader5 : require // uint64_t \n" +"//#extension GL_ARB_gpu_shader_int64: require // uint64_t \n" +"in float osg_FrameTime; \n" +"out vec2 TexCoord; \n" +"flat out int textureIndex; \n" +"void main() { \n" +" mat4 scale =mat4(0.01, 0.00, 0.00, 0.00, \n" +" 0.00, 0.01, 0.00, 0.00, \n" +" 0.00, 0.00, 0.01, 0.00, \n" +" 0.00, 0.00, 0.00, 1.00); \n" +" vec4 pos = gl_Vertex*scale; \n" +" pos.x += ((gl_InstanceID%100)/10)*0.015-0.075; \n" +" pos.y += (gl_InstanceID/100)*0.015 - 0.075; \n" +" pos.z += (gl_InstanceID%10)*0.015 - 0.075; \n" +" pos.w = 1; \n" +" gl_Position = gl_ModelViewProjectionMatrix*pos; \n" +" TexCoord = gl_MultiTexCoord0.xy; \n" +" textureIndex = gl_InstanceID%XXX; \n" +"} \n" +; +//we could setup tex to be of type sampler2D, and not have to do +//the type conversion, but I wanted to added code to test if tex +//had a value set. +//If we get a red cube, we are not getting our handle from our UBO, +//if we get a black cube, then we are having an issue with the +//texture handle itself +std::string fragShader = +"#version 450 compatibility \n" +"#extension GL_ARB_bindless_texture : require \n" +"#extension GL_NV_gpu_shader5 : require // uint64_t \n" +"//#extension GL_ARB_gpu_shader5 : require // uint64_t \n" +"//#extension GL_ARB_gpu_shader_int64: require // uint64_t \n" +"uniform sampler2D TextureId; \n" +"in vec2 TexCoord; \n" +"flat in int textureIndex; \n" +"layout (binding = 0, std140) uniform TEXTURE_BLOCK \n" +"{ \n" +" uint64_t tex[XXX]; \n" +"}; \n" +"void main() { \n" +" int tIndex = (int)(textureIndex); \n" +" sampler2D myText = sampler2D(tex[tIndex]); \n" +" gl_FragColor = texture2D(myText,TexCoord); \n" +" if (tex[tIndex] == 0) gl_FragColor.r = 1.0; \n" +"} \n" +; + +///This class provides a basic wraper for a Uniform Buffer Object +///or UBO, and provides the storage for the texture handles +class BindlessBuffer: public osg::Referenced{ +public: + typedef osg::ref_ptr UniBufferObjRef; + typedef osg::ref_ptr UniBufferBindingRef; + typedef osg::ref_ptr HandleArrayRef; + typedef osg::ref_ptr BindlessBufferRef; + static BindlessBufferRef Make(size_t count){ + BindlessBufferRef val = new BindlessBuffer(); + val->_count = count; + val->_sbbo = new osg::UniformBufferObject; + val->_handles = new osg::UInt64Array(); + val->_handles->resize(count*2,0); + val->_handles->setBufferObject(val->_sbbo); + val->_ssbb = new osg::UniformBufferBinding(0, val->_sbbo.get(), 0, sizeof(GLuint64)*count); + return val; + } + BindlessBuffer& operator = (const BindlessBuffer& rhs){ + if (this != &rhs){ + _count=rhs._count; + _sbbo =rhs._sbbo ; + _ssbb =rhs._ssbb ; + _handles = rhs._handles; + } + return *this; + } + BindlessBuffer(const BindlessBuffer& rhs):osg::Referenced(rhs){ + if (this != &rhs){ + _count=rhs._count; + _sbbo =rhs._sbbo ; + _ssbb =rhs._ssbb ; + _handles = rhs._handles; + } + } + UniBufferObjRef& Object(){return _sbbo;} + UniBufferBindingRef& Binding(){return _ssbb;} + HandleArrayRef& Handles(){return _handles;} + int count(){return _count;} +private: + int _count; + UniBufferObjRef _sbbo; + UniBufferBindingRef _ssbb; + HandleArrayRef _handles; + + BindlessBuffer():osg::Referenced(),_count(0){ + } +}; + +///This class extends a Texture, when this is texture is applied +///the first time, it will setup all our texture handles, after that +///it will not make any more GL calls until it gets released +class BindlessTexture: public osg::Texture2D +{ +public: + typedef osg::ref_ptr BufferRef; + typedef std::vector > TextureList; + typedef std::vector HandleList; + typedef osg::ref_ptr< osg::Texture::TextureObject> TextureObjectRef; + typedef std::vector TextureObjectList; + typedef osg::buffered_object TextureObjectBuffer; + + BindlessTexture(); + BindlessTexture(BufferRef, TextureList); + BindlessTexture(const BindlessTexture& rhs, const osg::CopyOp& copy =osg::CopyOp::SHALLOW_COPY); + void releaseGLObjects(osg::State* state) const; + void resizeGLObjectBuffers(unsigned maxSize); + void setBidlessIndex(unsigned int index); + META_StateAttribute(osg, BindlessTexture, TEXTURE); + + void apply(osg::State& state) const; +protected: + void applyOnce(osg::State &state) const; + mutable osg::buffered_object _handles; + mutable TextureList _textureList; + mutable osg::ref_ptr _buffer; + mutable std::vector _isBound; + mutable TextureObjectBuffer _textureBufferList; + // array index = texture image unit. + unsigned int _bindlessIndex; +}; + + +BindlessTexture::BindlessTexture():osg::Texture2D(),_bindlessIndex(0) +{ + _isBound.resize(5,false); +} + +BindlessTexture::BindlessTexture(const BindlessTexture& rhs, const osg::CopyOp& copy) + :osg::Texture2D( rhs, copy ) +{ + _isBound.resize(5,false); + _buffer = rhs._buffer; + _bindlessIndex = rhs._bindlessIndex; + for(unsigned i=0; i image = _image; + if (_handles[contextID].size() < _textureList.size()) + _handles[contextID].resize( _textureList.size(),0); + if (_textureBufferList[contextID].size() < _textureList.size()) + _textureBufferList[contextID].resize( _textureList.size()); + int txtcount = _textureList.size(); + if (_buffer->count() < txtcount) + txtcount = _buffer->count(); + //for each actual texture we have, bind it, get the texture hande, assign the value to our UBO + for (int i = 0; i bind(); + applyTexParameters(GL_TEXTURE_2D,state); + + applyTexImage2D_load(state,GL_TEXTURE_2D,image.get(), + _textureWidth, _textureHeight, _numMipmapLevels); + textureObject->setAllocated(true); + _textureBufferList[contextID][i] = textureObject; + + //Here is where the "magic" happens, we get the texture handle for our texture, copy it to our UBO, + //and then tell OpenGL to keep the handle resident + _handles[contextID][i] = extensions->glGetTextureHandle( textureObject->id() ); + std::vector &vec = _buffer->Handles()->asVector(); + vec[i*2] = _handles[contextID][i]; + _buffer->Object()->dirty(); + _buffer->Handles()->dirty(); + + if ( _handles[contextID][i] != 0L || extensions->glIsTextureHandleResident( _handles[contextID][i]) == GL_FALSE) + { + extensions->glMakeTextureHandleResident( _handles[contextID][i] ); + } + } + + // update the modified tag to show that it is up to date. + getModifiedCount(contextID) = image->getModifiedCount(); +} + +void BindlessTexture::apply(osg::State& state) const +{ + unsigned contextID = state.getContextID(); + if ( _isBound[contextID] == false ) + { + applyOnce(state); + _isBound[contextID] = true; + }else{ + //we should mostly hit this durring the lifetime of this object, + //note we basically do nothing...... + } +} +/// cleanup, we just need to tell OpenGL to release our texture handle +void BindlessTexture::releaseGLObjects(osg::State* state) const +{ + if ( state ) + { + unsigned contextID = state->getContextID(); + osg::Texture2D::releaseGLObjects( state ); + osg::GLExtensions* ext = osg::GLExtensions::Get( contextID, true ); + + for(unsigned i=0; i<_handles[contextID].size(); ++i) + { + ext->glMakeTextureHandleNonResident( _handles[contextID][i] ); + _handles[contextID][i] = 0; + } + + } +} + +void +BindlessTexture::resizeGLObjectBuffers(unsigned maxSize) +{ + osg::Texture2D::resizeGLObjectBuffers( maxSize ); + + size_t handleSize = _handles.size(); + size_t txtSize = _textureList.size(); + size_t boundSize = _isBound.size(); + if ( handleSize < maxSize ) { + _isBound.resize(maxSize,false); + } + if ( handleSize < maxSize ) { + _handles.resize( maxSize ); + for(unsigned i=handleSize; i<_handles.size(); ++i){ + for(unsigned j=0; j ImageRef; +/////////////////////////////////////////////////////// +///Create an array of images, with checkerboard +///pattern with random color and size +/// +void createImageArray(osg::StateSet* attachPnt){ + BindlessTexture::TextureList images; + images.resize(TextureCount); + BindlessBuffer::BindlessBufferRef buffer = BindlessBuffer::Make(TextureCount); + srand (time(NULL)); + for (int i =0; i < TextureCount; i++){ + ImageRef tImage = new osg::Image(); + int powerOf2 = rand()%6+4; + const unsigned int imageSize = 1<allocateImage(imageSize,imageSize,1,GL_RGBA,GL_UNSIGNED_BYTE); + unsigned char* buff = tImage->data(); + const int stride = 4; + unsigned char primaryColor[4]; + + int boxWidth = rand()%15+2; + int boxLength = rand()%15+2; + + //light squares + primaryColor[0] = rand()%128 + 128; + primaryColor[1] = rand()%128 + 128; + primaryColor[2] = rand()%128 + 128; + //dark squares + unsigned char secondaryColor[4]; + secondaryColor[0] = rand()%128; + secondaryColor[1] = rand()%128; + secondaryColor[2] = rand()%128; + for (int x = 0; x < imageSize; x++){ + for (int y =0; y0; + if (isPrimaryColor){ + pixel[0] = primaryColor[0]; + pixel[1] = primaryColor[1]; + pixel[2] = primaryColor[2]; + }else{ + pixel[0] = secondaryColor[0]; + pixel[1] = secondaryColor[1]; + pixel[2] = secondaryColor[2]; + } + pixel[3] = 255; + } + } + images[i] = tImage; + + std::stringstream sstr; + sstr<<"Image"<setName(sstr.str()); + } + BindlessTexture* tex = new BindlessTexture(buffer,images); + attachPnt->setTextureAttribute(0,tex,osg::StateAttribute::ON); + attachPnt->setAttributeAndModes(buffer->Binding(), osg::StateAttribute::ON); +} +///Create a cube centered at the origin, with given by size +/// +osg::Geometry* createCube(float scale, osg::Vec3 origin = osg::Vec3(0.0f,0.0f,0.0f) ) +{ + osg::Geometry* geometry = new osg::Geometry; + geometry->setName("TexturedCubeArray"); + + osg::Vec3Array* vertices = new osg::Vec3Array; + geometry->setVertexArray(vertices); + + osg::Vec2Array* tcoords = new osg::Vec2Array(); + geometry->setTexCoordArray(0,tcoords); + + origin -= osg::Vec3(scale/2.0f,scale/2.0f,scale/2.0f); + osg::Vec3 dx(scale,0.0f,0.0f); + osg::Vec3 dy(0.0f,scale,0.0f); + osg::Vec3 dz(0.0f,0.0f,scale); + + { + // front face + vertices->push_back(origin); + vertices->push_back(origin+dx); + vertices->push_back(origin+dx+dz); + vertices->push_back(origin+dz); + + tcoords->push_back(osg::Vec2(0.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,1.0f)); + tcoords->push_back(osg::Vec2(0.0f,1.0f)); + } + + { + // back face + vertices->push_back(origin+dy); + vertices->push_back(origin+dy+dz); + vertices->push_back(origin+dy+dx+dz); + vertices->push_back(origin+dy+dx); + + tcoords->push_back(osg::Vec2(0.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,1.0f)); + tcoords->push_back(osg::Vec2(0.0f,1.0f)); + } + + { + // left face + vertices->push_back(origin+dy); + vertices->push_back(origin); + vertices->push_back(origin+dz); + vertices->push_back(origin+dy+dz); + + tcoords->push_back(osg::Vec2(0.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,1.0f)); + tcoords->push_back(osg::Vec2(0.0f,1.0f)); + } + + { + // right face + vertices->push_back(origin+dx+dy); + vertices->push_back(origin+dx+dy+dz); + vertices->push_back(origin+dx+dz); + vertices->push_back(origin+dx); + + tcoords->push_back(osg::Vec2(0.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,1.0f)); + tcoords->push_back(osg::Vec2(0.0f,1.0f)); + } + + { + // top face + vertices->push_back(origin+dz); + vertices->push_back(origin+dz+dx); + vertices->push_back(origin+dz+dx+dy); + vertices->push_back(origin+dz+dy); + + tcoords->push_back(osg::Vec2(0.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,1.0f)); + tcoords->push_back(osg::Vec2(0.0f,1.0f)); + } + + { + // bottom face + vertices->push_back(origin); + vertices->push_back(origin+dy); + vertices->push_back(origin+dx+dy); + vertices->push_back(origin+dx); + + tcoords->push_back(osg::Vec2(0.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,0.0f)); + tcoords->push_back(osg::Vec2(1.0f,1.0f)); + tcoords->push_back(osg::Vec2(0.0f,1.0f)); + } + osg::DrawArrays* primSet = new osg::DrawArrays(GL_QUADS, 0, vertices->size()); + geometry->addPrimitiveSet(primSet); + + return geometry; +} +/// +///Here we are going to create our scene, basically we need to make sure our +///Bindless texture gets applied before our shader programs. +/// +/// +osg::Group* CreateScene(){ + osg::Group* sceneRoot= new osg::Group(); + sceneRoot->setName("Root"); + osg::Geode *geo = new osg::Geode(); + geo->setName("Geo"); + sceneRoot->addChild(geo); + float size = 1.0f; + osg::StateSet* scene_ss = sceneRoot->getOrCreateStateSet(); + createImageArray(scene_ss); + scene_ss->setMode(GL_DEPTH_TEST,osg::StateAttribute::ON); + + osg::ref_ptr geom = createCube(0.9f); + osg::PrimitiveSet *prim = geom->getPrimitiveSet(0); + //instanced elements must use VBOs + geom->setUseDisplayList(false); + geom->setUseVertexBufferObjects(true); + geom->setCullingActive(false); + prim->setNumInstances(1000); + prim->dirty(); + sceneRoot->addChild(geo); + geo->addDrawable(geom.get()); + + osg::StateSet* ss = geo->getOrCreateStateSet(); + + std::string strTextureCount; + std::stringstream ssconv; + ssconv<>strTextureCount; + std::string::size_type pos = vertShader.find("XXX"); + vertShader.replace(pos,size_t(3),strTextureCount); + + pos = fragShader.find("XXX"); + fragShader.replace(pos,size_t(3),strTextureCount); + + osg::Program* program = new osg::Program; + osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertShader); + program->addShader(vertex_shader); + + osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragShader); + program->addShader(fragment_shader); + + ss->setAttributeAndModes(program, osg::StateAttribute::ON); + + + return sceneRoot; + } +int main(int argc, char** argv) +{ + // set command line options + osg::ArgumentParser arguments(&argc,argv); + + // construct the viewer. + osgViewer::Viewer viewer(arguments); + + // add the stats handler + viewer.addEventHandler(new osgViewer::StatsHandler); + + // add model to viewer. + viewer.setSceneData( CreateScene() ); + + viewer.getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true); + + return viewer.run(); +} +