/*========================================================================= 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 "vtk_glew.h" #include "vtkObjectFactory.h" #include "vtkOpenGLRenderWindow.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] = { GL_STREAM_DRAW, GL_STREAM_READ, GL_STREAM_COPY, GL_STATIC_DRAW, GL_STATIC_READ, GL_STATIC_COPY, GL_DYNAMIC_DRAW, GL_DYNAMIC_READ, GL_DYNAMIC_COPY }; static const char* BufferObjectUsageAsString[9] = { "StreamDraw", "StreamRead", "StreamCopy", "StaticDraw", "StaticRead", "StaticCopy", "DynamicDraw", "DynamicRead", "DynamicCopy" }; // access modes const GLenum OpenGLBufferObjectAccess[2] = { #ifdef GL_ES_VERSION_3_0 GL_MAP_WRITE_BIT, GL_MAP_READ_BIT #else GL_WRITE_ONLY, GL_READ_ONLY #endif }; // targets const GLenum OpenGLBufferObjectTarget[2] = { GL_PIXEL_UNPACK_BUFFER, GL_PIXEL_PACK_BUFFER }; #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 vtksizeof { public: static int GetSize() { return sizeof(T); } }; template <> class vtksizeof { 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 = nullptr; 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*) { return true; } //------------------------------------------------------------------------------ bool vtkPixelBufferObject::LoadRequiredExtensions(vtkRenderWindow* vtkNotUsed(renWin)) { return true; } //------------------------------------------------------------------------------ void vtkPixelBufferObject::SetContext(vtkRenderWindow* renWin) { // avoid pointless re-assignment if (this->Context == renWin) { return; } // free resource allocations this->DestroyBuffer(); this->Context = nullptr; this->Modified(); // all done if assigned null if (!renWin) { 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 = GL_PIXEL_PACK_BUFFER; break; case vtkPixelBufferObject::UNPACKED_BUFFER: target = GL_PIXEL_UNPACK_BUFFER; break; default: vtkErrorMacro("Impossible BufferType."); target = static_cast(this->BufferTarget); break; } if (this->BufferTarget && this->BufferTarget != target) { this->UnBind(); } this->BufferTarget = target; glBindBuffer(static_cast(this->BufferTarget), this->Handle); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); } //------------------------------------------------------------------------------ void vtkPixelBufferObject::UnBind() { assert(this->Context); if (this->Handle && this->BufferTarget) { glBindBuffer(this->BufferTarget, 0); vtkOpenGLCheckErrorMacro("failed at glBindBuffer(0)"); this->BufferTarget = 0; } } //------------------------------------------------------------------------------ void vtkPixelBufferObject::CreateBuffer() { if (!this->Handle) { GLuint ioBuf; glGenBuffers(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); glDeleteBuffers(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 = nullptr; if (components == 0) { numComp = numComponents; permutation = new int[numComponents]; int i = 0; while (i < numComp) { permutation[i] = i; ++i; } } else { numComp = components; permutation = componentList; } vtkIdType tupleSize = static_cast(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 glBindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); glBufferData(target, size, nullptr, usage); vtkOpenGLCheckErrorMacro("failed at glBufferData"); #ifdef GL_ES_VERSION_3_0 void* pPBO = glMapBufferRange(target, 0, size, access); #else void* pPBO = glMapBuffer(target, access); #endif vtkOpenGLCheckErrorMacro("failed at glMapBuffer"); glBindBuffer(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 nullptr; } GLenum target = OpenGLBufferObjectTarget[mode]; GLenum access = OpenGLBufferObjectAccess[mode]; // pointer to the mnapped memory glBindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); #ifdef GL_ES_VERSION_3_0 void* pPBO = glMapBufferRange(this->BufferTarget, 0, this->Size, access); #else void* pPBO = glMapBuffer(target, access); #endif vtkOpenGLCheckErrorMacro("failed at glMapBuffer"); glBindBuffer(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]; glBindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); glUnmapBuffer(target); vtkOpenGLCheckErrorMacro("failed at glUnmapBuffer"); glBindBuffer(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 != nullptr) { this->Usage = StreamDraw; } else { this->Usage = StreamRead; } glBufferData(this->BufferTarget, size * static_cast(::vtkGetSize(type)), nullptr, OpenGLBufferObjectUsage[this->Usage]); vtkOpenGLCheckErrorMacro("failed at glBufferData"); this->Type = type; if (this->Type == VTK_DOUBLE) { this->Type = VTK_FLOAT; } this->Size = size; if (data) { #ifdef GL_ES_VERSION_3_0 void* ioMem = glMapBufferRange(this->BufferTarget, 0, size, GL_MAP_WRITE_BIT); #else void* ioMem = glMapBuffer(this->BufferTarget, GL_WRITE_ONLY); #endif vtkOpenGLCheckErrorMacro(""); switch (type) { vtkTemplateMacro(::vtkUpload3D::Upload(ioMem, static_cast(data), dims, numComponents, continuousIncrements, components, componentList);); default: vtkErrorMacro("unsupported vtk type"); return false; } glUnmapBuffer(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" << time << " seconds." << endl; #endif return true; } //------------------------------------------------------------------------------ void vtkPixelBufferObject::Allocate(int type, unsigned int numtuples, int comps, BufferType mode) { assert(this->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 numbytes, BufferType mode) { assert(this->Context); // from vtk to opengl enums GLenum target = OpenGLBufferObjectTarget[mode]; GLenum usage = OpenGLBufferObjectUsage[mode]; GLuint size = static_cast(numbytes); GLuint ioBuf = static_cast(this->Handle); if (!ioBuf) { glGenBuffers(1, &ioBuf); vtkOpenGLCheckErrorMacro("failed at glGenBuffers"); this->Handle = static_cast(ioBuf); } this->BufferTarget = 0; glBindBuffer(target, ioBuf); vtkOpenGLCheckErrorMacro("failed at glBindBuffer"); glBufferData(target, size, nullptr, usage); vtkOpenGLCheckErrorMacro("failed at glBufferData"); glBindBuffer(target, 0); } //------------------------------------------------------------------------------ void vtkPixelBufferObject::ReleaseMemory() { assert(this->Context); assert(this->Handle); this->Bind(vtkPixelBufferObject::PACKED_BUFFER); glBufferData(this->BufferTarget, 0, nullptr, GL_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); #ifdef GL_ES_VERSION_3_0 void* ioMem = glMapBufferRange(this->BufferTarget, 0, this->Size, GL_MAP_READ_BIT); #else void* ioMem = glMapBuffer(this->BufferTarget, GL_READ_ONLY); #endif 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; } glUnmapBuffer(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" << time << " seconds." << endl; #endif return true; } //------------------------------------------------------------------------------ void vtkPixelBufferObject::PrintSelf(ostream& os, vtkIndent indent) { this->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; }