/*========================================================================= Program: Visualization Toolkit Module: vtkPixelBufferObject.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkPixelBufferObject.h" #include "vtkObjectFactory.h" #include "vtkOpenGLExtensionManager.h" #include "vtkOpenGLRenderWindow.h" #include "vtkRenderWindow.h" #include "vtkgl.h" #include "vtkOpenGL.h" #include "vtkOpenGLError.h" //#define VTK_PBO_DEBUG //#define VTK_PBO_TIMING #ifdef VTK_PBO_TIMING #include "vtkTimerLog.h" #endif #include // Mapping from Usage values to OpenGL values. static const GLenum OpenGLBufferObjectUsage[9]= { vtkgl::STREAM_DRAW, vtkgl::STREAM_READ, vtkgl::STREAM_COPY, vtkgl::STATIC_DRAW, vtkgl::STATIC_READ, vtkgl::STATIC_COPY, vtkgl::DYNAMIC_DRAW, vtkgl::DYNAMIC_READ, vtkgl::DYNAMIC_COPY }; static const char *BufferObjectUsageAsString[9]= { "StreamDraw", "StreamRead", "StreamCopy", "StaticDraw", "StaticRead", "StaticCopy", "DynamicDraw", "DynamicRead", "DynamicCopy" }; // access modes const GLenum OpenGLBufferObjectAccess[2]= { vtkgl::WRITE_ONLY, vtkgl::READ_ONLY }; // targets const GLenum OpenGLBufferObjectTarget[2]= { vtkgl::PIXEL_UNPACK_BUFFER_ARB, vtkgl::PIXEL_PACK_BUFFER_ARB }; #ifdef VTK_PBO_DEBUG #include // for debugging with MPI, pthread_self() #endif // converting double to float behind the // scene so we need sizeof(double)==4 template< class T > class vtksizeof { public: static int GetSize() { return sizeof(T); } }; template<> class vtksizeof< double > { public: static int GetSize() { return sizeof(float); } }; static int vtkGetSize(int type) { switch (type) { vtkTemplateMacro( return ::vtksizeof::GetSize(); ); } return 0; } //---------------------------------------------------------------------------- vtkStandardNewMacro(vtkPixelBufferObject); //---------------------------------------------------------------------------- vtkPixelBufferObject::vtkPixelBufferObject() { this->Handle = 0; this->Context = NULL; this->BufferTarget = 0; this->Components = 0; this->Size = 0; this->Type = VTK_UNSIGNED_CHAR; this->Usage = StaticDraw; } //---------------------------------------------------------------------------- vtkPixelBufferObject::~vtkPixelBufferObject() { this->DestroyBuffer(); } //---------------------------------------------------------------------------- bool vtkPixelBufferObject::IsSupported(vtkRenderWindow* win) { vtkOpenGLRenderWindow* renWin = vtkOpenGLRenderWindow::SafeDownCast(win); if (renWin) { vtkOpenGLExtensionManager* mgr = renWin->GetExtensionManager(); bool vbo=mgr->ExtensionSupported("GL_VERSION_1_5") || mgr->ExtensionSupported("GL_ARB_vertex_buffer_object"); // pbo extension does not define new functions but uses functions defined // by vbo extension. bool pbo=mgr->ExtensionSupported("GL_VERSION_2_1") || mgr->ExtensionSupported("GL_ARB_pixel_buffer_object"); return vbo && pbo; } return false; } //---------------------------------------------------------------------------- bool vtkPixelBufferObject::LoadRequiredExtensions(vtkRenderWindow *renWin) { vtkOpenGLRenderWindow* context = vtkOpenGLRenderWindow::SafeDownCast(renWin); if ( !context ) return false; vtkOpenGLExtensionManager* mgr = context->GetExtensionManager(); bool gl15=mgr->ExtensionSupported("GL_VERSION_1_5")==1; bool gl21=mgr->ExtensionSupported("GL_VERSION_2_1")==1; bool vbo=gl15 || mgr->ExtensionSupported("GL_ARB_vertex_buffer_object"); // pbo extension does not define new functions but uses functions defined // by vbo extension. bool pbo=gl21 || mgr->ExtensionSupported("GL_ARB_pixel_buffer_object"); bool result=vbo && pbo; if(result) { if(gl15) { mgr->LoadExtension("GL_VERSION_1_5"); } else { mgr->LoadCorePromotedExtension("GL_ARB_vertex_buffer_object"); } // pbo does not define new functions, nothing to do here. } return result; } //---------------------------------------------------------------------------- void vtkPixelBufferObject::SetContext(vtkRenderWindow* renWin) { // avoid pointless re-assignment if (this->Context==renWin) { return; } // free resource allocations this->DestroyBuffer(); this->Context = NULL; this->Modified(); // all done if assigned null if (!renWin) { return; } // check for support vtkOpenGLRenderWindow* context = vtkOpenGLRenderWindow::SafeDownCast(renWin); if ( !context || !this->LoadRequiredExtensions(renWin) ) { vtkErrorMacro("Required OpenGL extensions not supported by the context."); return; } // update context this->Context = renWin; this->Context->MakeCurrent(); } //---------------------------------------------------------------------------- vtkRenderWindow* vtkPixelBufferObject::GetContext() { return this->Context; } //---------------------------------------------------------------------------- void vtkPixelBufferObject::SetSize(unsigned int nTups, int nComps) { this->Size = nTups*nComps; } //---------------------------------------------------------------------------- void vtkPixelBufferObject::Bind(BufferType type) { assert(this->Context); this->CreateBuffer(); GLenum target; switch (type) { case vtkPixelBufferObject::PACKED_BUFFER: target = vtkgl::PIXEL_PACK_BUFFER_ARB; break; case vtkPixelBufferObject::UNPACKED_BUFFER: target = vtkgl::PIXEL_UNPACK_BUFFER_ARB; break; default: vtkErrorMacro("Impossible BufferType."); target = static_cast(this->BufferTarget); break; } if (this->BufferTarget && this->BufferTarget != target) { this->UnBind(); } this->BufferTarget = target; vtkgl::BindBuffer(static_cast(this->BufferTarget), this->Handle); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); } //---------------------------------------------------------------------------- void vtkPixelBufferObject::UnBind() { assert(this->Context); if (this->Handle && this->BufferTarget) { vtkgl::BindBuffer(this->BufferTarget, 0); vtkOpenGLCheckErrorMacro("failed at glBindBuffer(0)"); this->BufferTarget = 0; } } //---------------------------------------------------------------------------- void vtkPixelBufferObject::CreateBuffer() { if (!this->Handle) { GLuint ioBuf; vtkgl::GenBuffers(1, &ioBuf); vtkOpenGLCheckErrorMacro("failed at glGenBuffers"); this->Handle = ioBuf; } } //---------------------------------------------------------------------------- void vtkPixelBufferObject::DestroyBuffer() { // because we don't hold a reference to the render // context we don't have any control on when it is // destroyed. In fact it may be destroyed before // we are(eg smart pointers), in which case we should // do nothing. if (this->Context && this->Handle) { GLuint ioBuf = static_cast(this->Handle); vtkgl::DeleteBuffers(1, &ioBuf); vtkOpenGLCheckErrorMacro("failed at glDeleteBuffers"); } this->Handle = 0; } //---------------------------------------------------------------------------- template class vtkUpload3D { public: static void Upload(void *pboPtr, T *inData, unsigned int dims[3], int numComponents, vtkIdType continuousIncrements[3], int components, int *componentList) { // cout<<"incs[3]="<(pboPtr); int numComp; int *permutation=0; if(components==0) { numComp=numComponents; permutation=new int[numComponents]; int i=0; while(i(numComponents + continuousIncrements[0]); for (unsigned int zz=0; zz < dims[2]; zz++) { for (unsigned int yy = 0; yy < dims[1]; yy++) { for (unsigned int xx=0; xx < dims[0]; xx++) { for (int compNo=0; compNo < numComp; compNo++) { *fIoMem = inData[permutation[compNo]]; // cout<<"upload[zz="<Handle = static_cast(ioBuf); } this->BufferTarget = 0; // pointer to the mapped memory vtkgl::BindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); vtkgl::BufferData(target, size, NULL, usage); vtkOpenGLCheckErrorMacro("failed at glBufferData"); void *pPBO = vtkgl::MapBuffer(target, access); vtkOpenGLCheckErrorMacro("failed at glMapBuffer"); vtkgl::BindBuffer(target, 0); return pPBO; } //---------------------------------------------------------------------------- void *vtkPixelBufferObject::MapBuffer( int type, unsigned int numtuples, int comps, BufferType mode) { // from vtk to opengl enums this->Size = numtuples*comps; this->Type = type; this->Components = comps; unsigned int size = ::vtkGetSize(type)*this->Size; return this->MapBuffer(size, mode); } //---------------------------------------------------------------------------- void *vtkPixelBufferObject::MapBuffer(BufferType mode) { // from vtk to opengl enum GLuint ioBuf = static_cast(this->Handle); if (!ioBuf) { vtkErrorMacro("Uninitialized object"); return NULL; } GLenum target = OpenGLBufferObjectTarget[mode]; GLenum access = OpenGLBufferObjectAccess[mode]; // pointer to the mnapped memory vtkgl::BindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); void *pPBO = vtkgl::MapBuffer(target, access); vtkOpenGLCheckErrorMacro("failed at glMapBuffer"); vtkgl::BindBuffer(target, 0); vtkOpenGLCheckErrorMacro("failed at glBindBuffer(0)"); this->BufferTarget = 0; return pPBO; } //---------------------------------------------------------------------------- void vtkPixelBufferObject::UnmapBuffer(BufferType mode) { GLuint ioBuf = static_cast(this->Handle); if (!ioBuf) { vtkErrorMacro("Uninitialized object"); return; } GLenum target = OpenGLBufferObjectTarget[mode]; vtkgl::BindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); vtkgl::UnmapBuffer(target); vtkOpenGLCheckErrorMacro("failed at glUnmapBuffer"); vtkgl::BindBuffer(target, 0); vtkOpenGLCheckErrorMacro("failed at glBindBuffer(0)"); } //---------------------------------------------------------------------------- bool vtkPixelBufferObject::Upload3D( int type, void* data, unsigned int dims[3], int numComponents, vtkIdType continuousIncrements[3], int components, int *componentList) { #ifdef VTK_PBO_TIMING vtkTimerLog *timer=vtkTimerLog::New(); timer->StartTimer(); #endif assert(this->Context); this->CreateBuffer(); this->Bind(vtkPixelBufferObject::UNPACKED_BUFFER); unsigned int size; if(components==0) { size = dims[0]*dims[1]*dims[2]*static_cast(numComponents); } else { size = dims[0]*dims[1]*dims[2]*static_cast(components); } this->Components = numComponents; if(data!=0) { this->Usage=StreamDraw; } else { this->Usage=StreamRead; } vtkgl::BufferData(this->BufferTarget, size*static_cast(::vtkGetSize(type)), NULL,OpenGLBufferObjectUsage[this->Usage]); vtkOpenGLCheckErrorMacro("failed at glBufferData"); this->Type = type; if (this->Type == VTK_DOUBLE) { this->Type = VTK_FLOAT; } this->Size = size; if (data) { void* ioMem = vtkgl::MapBuffer(this->BufferTarget, vtkgl::WRITE_ONLY); vtkOpenGLCheckErrorMacro(""); switch (type) { vtkTemplateMacro( ::vtkUpload3D< VTK_TT >::Upload(ioMem, static_cast(data), dims, numComponents, continuousIncrements, components,componentList); ); default: vtkErrorMacro("unsupported vtk type"); return false; } vtkgl::UnmapBuffer(this->BufferTarget); vtkOpenGLCheckErrorMacro("failed at glUnmapBuffer"); } this->UnBind(); #ifdef VTK_PBO_TIMING timer->StopTimer(); double time=timer->GetElapsedTime(); timer->Delete(); cout<<"Upload data to PBO"<Context); // from vtk to opengl enums this->Size = numtuples*comps; this->Type = type; this->Components = comps; unsigned int size = ::vtkGetSize(type)*this->Size; this->Allocate(size, mode); } //---------------------------------------------------------------------------- void vtkPixelBufferObject::Allocate( unsigned int nbytes, BufferType mode) { assert(this->Context); // from vtk to opengl enums GLenum target = OpenGLBufferObjectTarget[mode]; GLenum usage = OpenGLBufferObjectUsage[mode]; GLuint size = static_cast(nbytes); GLuint ioBuf = static_cast(this->Handle); if (!ioBuf) { vtkgl::GenBuffers(1, &ioBuf); vtkOpenGLCheckErrorMacro("failed at glGenBuffers"); this->Handle = static_cast(ioBuf); } this->BufferTarget = 0; vtkgl::BindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); vtkgl::BufferData(target, size, NULL, usage); vtkOpenGLCheckErrorMacro("failed at glBufferData"); vtkgl::BindBuffer(target, 0); } //---------------------------------------------------------------------------- void vtkPixelBufferObject::ReleaseMemory() { assert(this->Context); assert(this->Handle); this->Bind(vtkPixelBufferObject::PACKED_BUFFER); vtkgl::BufferData(this->BufferTarget, 0, NULL, vtkgl::STREAM_DRAW); vtkOpenGLCheckErrorMacro("failed at glBufferData"); this->Size = 0; } // ---------------------------------------------------------------------------- template void vtkDownload3D(TPBO *pboPtr, TCPU *cpuPtr, unsigned int dims[3], int numcomps, vtkIdType increments[3]) { #ifdef VTK_PBO_DEBUG cout << "template vtkDownload3D" << endl; #endif vtkIdType tupleSize = static_cast(numcomps + increments[0]); for (unsigned int zz=0; zz < dims[2]; zz++) { for (unsigned int yy = 0; yy < dims[1]; yy++) { for (unsigned int xx=0; xx < dims[0]; xx++) { for (int comp=0; comp < numcomps; comp++) { *cpuPtr = static_cast(*pboPtr); // cout<<"download[zz="<(iData), odata, dims, numcomps, increments); ); default: #ifdef VTK_PBO_DEBUG cout << "d nested default." << endl; #endif break; } } //---------------------------------------------------------------------------- bool vtkPixelBufferObject::Download3D( int type, void* data, unsigned int dims[3], int numcomps, vtkIdType increments[3]) { #ifdef VTK_PBO_TIMING vtkTimerLog *timer=vtkTimerLog::New(); timer->StartTimer(); #endif assert(this->Context); if (!this->Handle) { vtkErrorMacro("No GPU data available."); return false; } if (this->Size < dims[0]*dims[1]*dims[2]*static_cast(numcomps)) { vtkErrorMacro("Size too small."); return false; } this->Bind(vtkPixelBufferObject::PACKED_BUFFER); void* ioMem = vtkgl::MapBuffer(this->BufferTarget, vtkgl::READ_ONLY); vtkOpenGLCheckErrorMacro("failed at glMapBuffer"); switch (type) { vtkTemplateMacro( VTK_TT* odata = static_cast(data); ::vtkDownload3DSpe(this->Type,ioMem,odata,dims,numcomps,increments); ); default: vtkErrorMacro("unsupported vtk type"); return false; } vtkgl::UnmapBuffer(this->BufferTarget); vtkOpenGLCheckErrorMacro("failed at glUnmapBuffer"); this->UnBind(); #ifdef VTK_PBO_TIMING timer->StopTimer(); double time=timer->GetElapsedTime(); timer->Delete(); cout<<"dowmload data from PBO"<Superclass::PrintSelf(os, indent); os << indent << "Context: " << this->Context << endl; os << indent << "Handle: " << this->Handle << endl; os << indent << "Size: " << this->Size << endl; os << indent << "VTK Type: " << vtkImageScalarTypeNameMacro(this->Type) << endl; os << indent << "Usage:" << BufferObjectUsageAsString[this->Usage] << endl; }