Fraxinus  17.12
An IGT application
cxGPUImageBuffer.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 
34 #include "cxGPUImageBuffer.h"
35 
36 #include <QString>
37 
38 #include <vector>
39 #include <vtkImageData.h>
40 #include <vtkPointData.h>
41 #include <vtkUnsignedCharArray.h>
42 #include <vtkUnsignedShortArray.h>
43 #include <boost/cstdint.hpp>
44 #include "cxGLHelpers.h"
45 #include "cxLogger.h"
46 
47 
48 #ifndef WIN32
49 #define GL_GLEXT_PROTOTYPES //Needed to compile on Linux?
50 //#include <vtkgl.h>
51 
52 #ifdef __APPLE__
53 #include <OpenGL/glu.h>
54 #else
55 #include <GL/glu.h>
56 #endif
57 
58 #ifdef WIN32
59 #include <windows.h>
60 #include <GL/glext.h>
61 #endif
62 
63 
64 namespace cx
65 {
66 
70 {
71 public:
72  GLuint textureId;
74  bool mAllocated;
75  uint64_t mMTime;
77 
79  {
80  mAllocated = false;
81  mMTime = 0;
82  textureId = 0;
83  mMemorySize = 0;
84  }
86  {
87  release();
88  }
89 
90  virtual int getMemorySize()
91  {
92  return mMemorySize;
93  }
94 
95  virtual void SetImage (vtkImageDataPtr texture)
96  {
97  if (!texture)
98  {
99  std::cout << "error: bad buffer initialization: null image" << std::endl;
100  }
101  mTexture = texture;
102  }
103  virtual unsigned int getTextureUid() const
104  {
105  return textureId;
106  }
107 
113  virtual void allocate(int textureUnitIndex)
114  {
115  if (mAllocated) // do this only once.
116  {
117  return;
118  }
119  if (!mTexture)
120  {
121  std::cout << "error: bad volume buffer initialization" << std::endl;
122  return;
123  }
124 
125 // glActiveTexture(GL_TEXTURE7);
126 // glActiveTexture(GL_TEXTURE0);
127  glActiveTexture(getGLTextureForVolume(textureUnitIndex));
128  report_gl_error();
129 
130 // glEnable(GL_TEXTURE_3D);
131  report_gl_error();
132  glGenTextures(1, &textureId);
133  report_gl_error();
134 // glDisable(GL_TEXTURE_3D);
135 
136  report_gl_error();
137  CX_LOG_DEBUG() << "textureId: " << textureId;
138 
139  updateTexture();
140  mAllocated = true;
141  }
142  virtual void updateTexture()
143  {
144  if (mMTime == mTexture->GetMTime())
145  {
146  return;
147  }
148  mMTime = mTexture->GetMTime();
149  //vtkgl::ActiveTexture(getGLTextureForVolume(textureUnitIndex)); //TODO is this OK?
150  GLenum size,internalType;
151  boost::uint32_t dimx = mTexture ->GetDimensions( )[0];
152  boost::uint32_t dimy = mTexture ->GetDimensions( )[1];
153  boost::uint32_t dimz = mTexture ->GetDimensions( )[2];
154  mMemorySize = dimx * dimy * dimz;
155 
156 
157  //Example: https://open.gl/textures
158 
159 // glEnable( GL_TEXTURE_3D );
160  glBindTexture(GL_TEXTURE_3D, textureId);
161  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );//GL_CLAMP is no longer allowed. Try with GL_CLAMP_TO_BORDER
162  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
163  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER );
164  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
165  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
166 // glGenerateMipmap(GL_TEXTURE_3D);
167  switch (mTexture->GetScalarType())
168  {
169  case VTK_UNSIGNED_CHAR:
170  {
171  size = GL_UNSIGNED_BYTE;
172  internalType = GL_RED;// GL_LUMINANCE is no longer used
173  }
174  break; //8UI_EXT; break;
175  case VTK_UNSIGNED_SHORT:
176  {
177  size = GL_UNSIGNED_SHORT;
178  internalType = GL_RED; //GL_LUMINANCE16 is deprecated
179  mMemorySize *= 2;
180  }
181  break; //16UI_EXT; break;
182  default:
183  size = 0;
184  internalType = 0;
185  std::cout << "Bit size not supported!" << std::endl;
186  QString dataType(mTexture->GetScalarTypeAsString());
187  CX_LOG_ERROR() << QString("Attempt to update 3D GL texture from type %1 failed. Only unsigned types supported").arg(dataType);
188  break;
189  }
190 
191  report_gl_error();
192  if (mTexture->GetNumberOfScalarComponents()==1)
193  {
194  void* data = mTexture->GetPointData()->GetScalars()->GetVoidPointer(0);
195  glTexImage3D(GL_TEXTURE_3D, 0, internalType, dimx, dimy, dimz, 0, GL_RED, size, data);
196  }
197  else if (mTexture->GetNumberOfScalarComponents()==3)
198  {
199  internalType = GL_RGB;
200  void* data = mTexture->GetPointData()->GetScalars()->GetVoidPointer(0);
201  glTexImage3D(GL_TEXTURE_3D, 0, internalType, dimx, dimy, dimz, 0, GL_RGB, size, data);
202  mMemorySize *= 3;
203  }
204  else
205  {
206  std::cout << "unsupported number of image components" << std::endl;
207  }
208 
209 // glDisable(GL_TEXTURE_3D);
210 
211  report_gl_error();
212  }
213 
218  virtual void bind(int textureUnitIndex)
219  {
220  glActiveTexture(getGLTextureForVolume(textureUnitIndex));
221  this->updateTexture();
222  if (!mAllocated)
223  {
224  std::cout << "error: called bind() on unallocated volume buffer" << std::endl;
225  return;
226  }
227  glBindTexture(GL_TEXTURE_3D, textureId);
228  report_gl_error();
229  }
230 
231  virtual void release()
232  {
233  glDeleteTextures(1, &textureId);
234  }
235 
236  int getGLTextureForVolume(int textureUnitIndex)
237  {
238  switch (textureUnitIndex)
239  {
240  case 0: return GL_TEXTURE0;
241  case 1: return GL_TEXTURE2;
242  case 2: return GL_TEXTURE4;
243  case 3: return GL_TEXTURE6;
244  case 4: return GL_TEXTURE8;
245  default: return -1;
246  }
247  }
248 };
249 
250 
254 {
255 public:
256  GLuint lutBuffer;
257  GLuint textureId;
260  uint64_t mMTime;
261 
263  {
264  mAllocated = false;
265  textureId = 0;
266  mMTime = 0;
267 // mDebugEnableCrossplatform=false;
268  }
270  {
271  release();
272  }
273 
274  virtual int getMemorySize()
275  {
276  if (!mTable)
277  return 0;
278  return mTable->GetNumberOfTuples() * mTable->GetNumberOfComponents() * sizeof(float);
279  }
280 
281 
282  //intput lookuptable is raw imported table
284  {
285  mTable = table;
286  }
287 
293  virtual void allocate()
294  {
295  if (!mTable)
296  {
297  std::cout << "error: bad lut buffer initialization" << std::endl;
298  return;
299  }
300 
301  if (!mAllocated)
302  {
303  glActiveTexture(GL_TEXTURE6);
304  glGenTextures(1, &textureId);
305  mAllocated = true;
306  }
307  this->updateTexture();
308  }
309 
310  virtual void updateTexture()
311  {
312  if (mMTime == mTable->GetMTime())
313  {
314  return;
315  }
316  mMTime = mTable->GetMTime();
317 
318  glActiveTexture(GL_TEXTURE6);
319  this->sendDataToGL();
320  report_gl_error();
321  }
322 
324  {
325  std::vector<float> lut;
326  int lutSize = mTable->GetNumberOfTuples();
327  int lutDataSize = lutSize * mTable->GetNumberOfComponents();
328  lut.resize(lutDataSize);
329  unsigned char* ptr = mTable->GetPointer(0);
330 
331  for (int i = 0; i < lut.size(); ++i)
332  {
333  lut[i] = ((float) *ptr) / 255.0;
334  ++ptr;
335  }
336 
337  glBindTexture(GL_TEXTURE_1D, textureId);
338  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
339  glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
340  glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
341  report_gl_error();
342 
343  glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA,
344  lutSize, 0,
345  GL_RGBA, GL_FLOAT, &(*lut.begin()));
346  report_gl_error();
347  }
348 
349  virtual void bind(int textureUnitIndex)
350  {
351  this->updateTexture();
352  if (!mAllocated)
353  {
354  std::cout << "error: called bind() on unallocated lut buffer" << std::endl;
355  return;
356  }
357  glActiveTexture(getGLTextureForLut(textureUnitIndex));
358  this->bindDataToGL();
359  report_gl_error();
360  }
361 
363  {
364  glBindTexture(GL_TEXTURE_1D, textureId);
365  }
366 
367  int getLutSize() const
368  {
369  return mTable->GetNumberOfTuples();
370  }
371 
372  virtual void release()
373  {
374  if (!mAllocated)
375  return;
376 
377  glDeleteTextures(1, &textureId);
378  }
379 
380  int getGLTextureForLut(int textureUnitIndex)
381  {
382  switch (textureUnitIndex)
383  {
384  case 0: return GL_TEXTURE1;
385  case 1: return GL_TEXTURE3;
386  case 2: return GL_TEXTURE5;
387  case 3: return GL_TEXTURE7;
388  case 4: return GL_TEXTURE9;
389  default: return -1;
390  }
391  }
392 };
393 
395 {
396  std::auto_ptr<GPUImageDataBufferImpl> retval(new GPUImageDataBufferImpl);
397  retval->SetImage(volume);
398  return GPUImageDataBufferPtr(retval.release());
399 }
400 
402 {
403  std::auto_ptr<GPUImageLutBufferImpl> retval(new GPUImageLutBufferImpl);
404  retval->SetColorMap(lut);
405  return GPUImageLutBufferPtr(retval.release());
406 }
407 
408 template<class BUFFER, class DATA_PTR>
409 boost::shared_ptr<BUFFER> createGPUImageBuffer(DATA_PTR val);
410 
411 template<>
412 boost::shared_ptr<GPUImageDataBuffer> createGPUImageBuffer<GPUImageDataBuffer>(vtkImageDataPtr val);
413 template<>
414 boost::shared_ptr<GPUImageLutBuffer> createGPUImageBuffer<GPUImageLutBuffer>(vtkUnsignedCharArrayPtr val);
415 
416 template<>
418 {
419  return createGPUImageDataBuffer(val);
420 }
421 template<>
422 boost::shared_ptr<GPUImageLutBuffer> createGPUImageBuffer<GPUImageLutBuffer>(vtkUnsignedCharArrayPtr val)
423 {
424  return createGPUImageLutBuffer(val);
425 }
426 
427 
428 //---------------------------------------------------------
429 GPUImageBufferRepository* GPUImageBufferRepository::mInstance = NULL;
430 //---------------------------------------------------------
431 
432 template<class DATA_PTR, class BUFFER>
434 {
435 public:
436  typedef boost::shared_ptr<BUFFER> BufferPtr;
437  typedef boost::weak_ptr<BUFFER> BufferWeakPtr;
438 
439  struct BufferStore
440  {
441  BufferStore(DATA_PTR data, BufferPtr buffer) : mData(data), mBuffer(buffer) {}
442  DATA_PTR mData;
443  BufferPtr mBuffer;
444  };
445 public:
446 
447  BufferQueue() : mMaxBuffers(7)
448  {
449 
450  }
451  void setMaxBuffers(unsigned val)
452  {
453  mMaxBuffers = val;
454  }
455  void setName(const QString& name)
456  {
457  mName = name;
458  }
459 
464  BufferPtr get(DATA_PTR data)
465  {
466  // clear out deleted data
467  for (typename BufferMap::iterator iter=mRemovedData.begin(); iter!=mRemovedData.end(); )
468  {
469  if (!iter->second.lock())
470  {
471  typename BufferMap::iterator temp = iter;
472  ++iter;
473  mRemovedData.erase(temp);
474  }
475  else
476  {
477  ++iter;
478  }
479  }
480 
481  // reclaim weak pointer to buffer if it exists.
482  if (mRemovedData.count(data))
483  {
484  BufferPtr object = mRemovedData[data].lock();
485  if (object)
486  {
487  mData.push_front(BufferStore(data, object));
488  mRemovedData.erase(data);
489  }
490  }
491 
492  // move current data to front of buffer (i.e. increase importance)
493  BufferPtr retval;
494  for (typename std::list<BufferStore>::iterator iter=mData.begin(); iter!=mData.end(); ++iter)
495  {
496  if (iter->mData==data)
497  {
498  retval = iter->mBuffer; // retrieve data
499  mData.push_front(*iter); // push on queue front (most recent)
500  mData.erase(iter); // erase from old position
501  break;
502  }
503  }
504 
505  // create buffer if nonexistent
506  if (!retval)
507  {
508  retval = createGPUImageBuffer<BUFFER>(data);
509  mData.push_front(BufferStore(data, retval));
510  }
511 
512  // reduce repository size if too large.
513  while (mData.size()>mMaxBuffers)
514  {
515  mRemovedData[mData.back().mData] = mData.back().mBuffer;
516  mData.pop_back();;
517  }
518 
519  return retval;
520  }
521  int getMemoryUsage(int *textures)
522  {
523  int mem = 0;
524  if (textures)
525  {
526  *textures = mData.size();
527  }
528  for (typename std::list<BufferStore>::iterator iter=mData.begin(); iter!=mData.end(); ++iter)
529  {
530  mem += iter->mBuffer->getMemorySize();
531  }
532  return mem;
533  }
534 private:
535  typedef std::map<DATA_PTR, BufferWeakPtr> BufferMap;
536  BufferMap mRemovedData; // those buffers that are removed but still might live outside of the repo.
537  std::list<BufferStore> mData; // newest elems in front
538  unsigned mMaxBuffers;
539  QString mName; // debug
540 };
541 
543 {
544 public:
546  {
547  mVolumeBuffer.setName("Volume");
548  mLutBuffer.setName("Lut");
549 
550  mVolumeBuffer.setMaxBuffers(12);
551  mLutBuffer.setMaxBuffers(15);
552  }
555 };
556 
557 GPUImageBufferRepository::GPUImageBufferRepository()
558 {
559  mInternal = new GPUImageBufferRepositoryInternal();
560 }
561 
562 GPUImageBufferRepository::~GPUImageBufferRepository()
563 {
564  tearDown();
565 }
566 
568 {
569  tearDown();
570  mInternal = new GPUImageBufferRepositoryInternal();
571 }
572 
574 {
575  if (!mInstance)
576  {
577  mInstance = new GPUImageBufferRepository();
578  }
579  return mInstance;
580 }
581 
583 {
584  if(mInstance)
585  {
586  delete mInstance;
587  }
588  mInstance = NULL;
589 
590 }
591 
592 void GPUImageBufferRepository::tearDown()
593 {
594  delete mInternal;
595  mInternal = NULL;
596 }
597 
599 {
600  return mInternal->mVolumeBuffer.getMemoryUsage(textures);
601 }
602 
604 {
605  return mInternal->mVolumeBuffer.get(volume);
606 }
607 
609 {
610  return mInternal->mLutBuffer.get(lut);
611 }
612 
614 {
616 }
617 }//cx
618 
619 #else
620 
621 namespace cx
622 {
624 void GPUImageBufferRepository::tearDown() {;}
625 }//namespace cx
626 
627 #endif //WIN32
Helper class for sharing GPU memory over several Views (GL contexts).
boost::shared_ptr< BUFFER > createGPUImageBuffer(DATA_PTR val)
BufferQueue< vtkImageDataPtr, GPUImageDataBuffer > mVolumeBuffer
boost::shared_ptr< GPUImageDataBuffer > createGPUImageBuffer< GPUImageDataBuffer, vtkImageDataPtr >(vtkImageDataPtr val)
GPUImageDataBufferPtr createGPUImageDataBuffer(vtkImageDataPtr volume)
vtkUnsignedCharArrayPtr mTable
virtual void SetColorMap(vtkUnsignedCharArrayPtr table)
virtual unsigned int getTextureUid() const
void setName(const QString &name)
BufferQueue< vtkUnsignedCharArrayPtr, GPUImageLutBuffer > mLutBuffer
Repository for GPU buffers.
virtual void allocate(int textureUnitIndex)
vtkSmartPointer< class vtkUnsignedCharArray > vtkUnsignedCharArrayPtr
boost::shared_ptr< GPUImageDataBuffer > createGPUImageBuffer< GPUImageDataBuffer >(vtkImageDataPtr val)
virtual void bind(int textureUnitIndex)
GPUImageLutBufferPtr getGPUImageLutBuffer(vtkUnsignedCharArrayPtr lut)
void setMaxBuffers(unsigned val)
virtual void SetImage(vtkImageDataPtr texture)
#define CX_LOG_ERROR
Definition: cxLogger.h:120
int getGLTextureForLut(int textureUnitIndex)
GPUImageLutBufferPtr createGPUImageLutBuffer(vtkUnsignedCharArrayPtr lut)
boost::shared_ptr< class GPUImageDataBuffer > GPUImageDataBufferPtr
boost::weak_ptr< BUFFER > BufferWeakPtr
#define CX_LOG_DEBUG
Definition: cxLogger.h:116
boost::shared_ptr< class GPUImageLutBuffer > GPUImageLutBufferPtr
int getGLTextureForVolume(int textureUnitIndex)
GPUImageDataBufferPtr getGPUImageDataBuffer(vtkImageDataPtr volume)
Helper class for sharing GPU memory over several Views (GL contexts).
static GPUImageBufferRepository * getInstance()
virtual void bind(int textureUnitIndex)
BufferStore(DATA_PTR data, BufferPtr buffer)
boost::shared_ptr< GPUImageLutBuffer > createGPUImageBuffer< GPUImageLutBuffer >(vtkUnsignedCharArrayPtr val)
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
boost::shared_ptr< BUFFER > BufferPtr
#define report_gl_error()
Definition: cxGLHelpers.h:49
int getMemoryUsage(int *textures)
GPUImageBufferRepository * getGPUImageBufferRepository()
Namespace for all CustusX production code.