Fraxinus  2023.01.05-dev+develop.0da12
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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 
13 #include "cxGPUImageBuffer.h"
14 
15 #include <QString>
16 
17 #include <vector>
18 #include <vtkImageData.h>
19 #include <vtkPointData.h>
20 #include <vtkUnsignedCharArray.h>
21 #include <vtkUnsignedShortArray.h>
22 #include <boost/cstdint.hpp>
23 #include "cxGLHelpers.h"
24 #include "cxLogger.h"
25 
26 
27 #ifndef WIN32
28 #define GL_GLEXT_PROTOTYPES //Needed to compile on Linux?
29 //#include <vtkgl.h>
30 
31 #ifdef __APPLE__
32 #include <OpenGL/glu.h>
33 #else
34 #include <GL/glu.h>
35 #endif
36 
37 #ifdef WIN32
38 #include <windows.h>
39 #include <GL/glext.h>
40 #endif
41 
42 
43 namespace cx
44 {
45 
49 {
50 public:
51  GLuint textureId;
53  bool mAllocated;
54  uint64_t mMTime;
56 
58  {
59  mAllocated = false;
60  mMTime = 0;
61  textureId = 0;
62  mMemorySize = 0;
63  }
65  {
66  release();
67  }
68 
69  virtual int getMemorySize()
70  {
71  return mMemorySize;
72  }
73 
74  virtual void SetImage (vtkImageDataPtr texture)
75  {
76  if (!texture)
77  {
78  std::cout << "error: bad buffer initialization: null image" << std::endl;
79  }
80  mTexture = texture;
81  }
82  virtual unsigned int getTextureUid() const
83  {
84  return textureId;
85  }
86 
92  virtual void allocate(int textureUnitIndex)
93  {
94  if (mAllocated) // do this only once.
95  {
96  return;
97  }
98  if (!mTexture)
99  {
100  std::cout << "error: bad volume buffer initialization" << std::endl;
101  return;
102  }
103 
104 // glActiveTexture(GL_TEXTURE7);
105 // glActiveTexture(GL_TEXTURE0);
106  glActiveTexture(getGLTextureForVolume(textureUnitIndex));
107  report_gl_error();
108 
109 // glEnable(GL_TEXTURE_3D);
110  report_gl_error();
111  glGenTextures(1, &textureId);
112  report_gl_error();
113 // glDisable(GL_TEXTURE_3D);
114 
115  report_gl_error();
116  CX_LOG_DEBUG() << "textureId: " << textureId;
117 
118  updateTexture();
119  mAllocated = true;
120  }
121  virtual void updateTexture()
122  {
123  if (mMTime == mTexture->GetMTime())
124  {
125  return;
126  }
127  mMTime = mTexture->GetMTime();
128  //vtkgl::ActiveTexture(getGLTextureForVolume(textureUnitIndex)); //TODO is this OK?
129  GLenum size,internalType;
130  boost::uint32_t dimx = mTexture ->GetDimensions( )[0];
131  boost::uint32_t dimy = mTexture ->GetDimensions( )[1];
132  boost::uint32_t dimz = mTexture ->GetDimensions( )[2];
133  mMemorySize = dimx * dimy * dimz;
134 
135 
136  //Example: https://open.gl/textures
137 
138 // glEnable( GL_TEXTURE_3D );
139  glBindTexture(GL_TEXTURE_3D, textureId);
140  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );//GL_CLAMP is no longer allowed. Try with GL_CLAMP_TO_BORDER
141  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
142  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER );
143  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
144  glTexParameteri( GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
145 // glGenerateMipmap(GL_TEXTURE_3D);
146  switch (mTexture->GetScalarType())
147  {
148  case VTK_UNSIGNED_CHAR:
149  {
150  size = GL_UNSIGNED_BYTE;
151  internalType = GL_RED;// GL_LUMINANCE is no longer used
152  }
153  break; //8UI_EXT; break;
154  case VTK_UNSIGNED_SHORT:
155  {
156  size = GL_UNSIGNED_SHORT;
157  internalType = GL_RED; //GL_LUMINANCE16 is deprecated
158  mMemorySize *= 2;
159  }
160  break; //16UI_EXT; break;
161  default:
162  size = 0;
163  internalType = 0;
164  std::cout << "Bit size not supported!" << std::endl;
165  QString dataType(mTexture->GetScalarTypeAsString());
166  CX_LOG_ERROR() << QString("Attempt to update 3D GL texture from type %1 failed. Only unsigned types supported").arg(dataType);
167  break;
168  }
169 
170  report_gl_error();
171  if (mTexture->GetNumberOfScalarComponents()==1)
172  {
173  void* data = mTexture->GetPointData()->GetScalars()->GetVoidPointer(0);
174  glTexImage3D(GL_TEXTURE_3D, 0, internalType, dimx, dimy, dimz, 0, GL_RED, size, data);
175  }
176  else if (mTexture->GetNumberOfScalarComponents()==3)
177  {
178  internalType = GL_RGB;
179  void* data = mTexture->GetPointData()->GetScalars()->GetVoidPointer(0);
180  glTexImage3D(GL_TEXTURE_3D, 0, internalType, dimx, dimy, dimz, 0, GL_RGB, size, data);
181  mMemorySize *= 3;
182  }
183  else
184  {
185  std::cout << "unsupported number of image components" << std::endl;
186  }
187 
188 // glDisable(GL_TEXTURE_3D);
189 
190  report_gl_error();
191  }
192 
197  virtual void bind(int textureUnitIndex)
198  {
199  glActiveTexture(getGLTextureForVolume(textureUnitIndex));
200  this->updateTexture();
201  if (!mAllocated)
202  {
203  std::cout << "error: called bind() on unallocated volume buffer" << std::endl;
204  return;
205  }
206  glBindTexture(GL_TEXTURE_3D, textureId);
207  report_gl_error();
208  }
209 
210  virtual void release()
211  {
212  glDeleteTextures(1, &textureId);
213  }
214 
215  int getGLTextureForVolume(int textureUnitIndex)
216  {
217  switch (textureUnitIndex)
218  {
219  case 0: return GL_TEXTURE0;
220  case 1: return GL_TEXTURE2;
221  case 2: return GL_TEXTURE4;
222  case 3: return GL_TEXTURE6;
223  case 4: return GL_TEXTURE8;
224  default: return -1;
225  }
226  }
227 };
228 
229 
233 {
234 public:
235  GLuint lutBuffer;
236  GLuint textureId;
239  uint64_t mMTime;
240 
242  {
243  mAllocated = false;
244  textureId = 0;
245  mMTime = 0;
246 // mDebugEnableCrossplatform=false;
247  }
249  {
250  release();
251  }
252 
253  virtual int getMemorySize()
254  {
255  if (!mTable)
256  return 0;
257  return mTable->GetNumberOfTuples() * mTable->GetNumberOfComponents() * sizeof(float);
258  }
259 
260 
261  //intput lookuptable is raw imported table
263  {
264  mTable = table;
265  }
266 
272  virtual void allocate()
273  {
274  if (!mTable)
275  {
276  std::cout << "error: bad lut buffer initialization" << std::endl;
277  return;
278  }
279 
280  if (!mAllocated)
281  {
282  glActiveTexture(GL_TEXTURE6);
283  glGenTextures(1, &textureId);
284  mAllocated = true;
285  }
286  this->updateTexture();
287  }
288 
289  virtual void updateTexture()
290  {
291  if (mMTime == mTable->GetMTime())
292  {
293  return;
294  }
295  mMTime = mTable->GetMTime();
296 
297  glActiveTexture(GL_TEXTURE6);
298  this->sendDataToGL();
299  report_gl_error();
300  }
301 
303  {
304  std::vector<float> lut;
305  int lutSize = mTable->GetNumberOfTuples();
306  int lutDataSize = lutSize * mTable->GetNumberOfComponents();
307  lut.resize(lutDataSize);
308  unsigned char* ptr = mTable->GetPointer(0);
309 
310  for (int i = 0; i < lut.size(); ++i)
311  {
312  lut[i] = ((float) *ptr) / 255.0;
313  ++ptr;
314  }
315 
316  glBindTexture(GL_TEXTURE_1D, textureId);
317  glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
318  glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
319  glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
320  report_gl_error();
321 
322  glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA,
323  lutSize, 0,
324  GL_RGBA, GL_FLOAT, &(*lut.begin()));
325  report_gl_error();
326  }
327 
328  virtual void bind(int textureUnitIndex)
329  {
330  this->updateTexture();
331  if (!mAllocated)
332  {
333  std::cout << "error: called bind() on unallocated lut buffer" << std::endl;
334  return;
335  }
336  glActiveTexture(getGLTextureForLut(textureUnitIndex));
337  this->bindDataToGL();
338  report_gl_error();
339  }
340 
342  {
343  glBindTexture(GL_TEXTURE_1D, textureId);
344  }
345 
346  int getLutSize() const
347  {
348  return mTable->GetNumberOfTuples();
349  }
350 
351  virtual void release()
352  {
353  if (!mAllocated)
354  return;
355 
356  glDeleteTextures(1, &textureId);
357  }
358 
359  int getGLTextureForLut(int textureUnitIndex)
360  {
361  switch (textureUnitIndex)
362  {
363  case 0: return GL_TEXTURE1;
364  case 1: return GL_TEXTURE3;
365  case 2: return GL_TEXTURE5;
366  case 3: return GL_TEXTURE7;
367  case 4: return GL_TEXTURE9;
368  default: return -1;
369  }
370  }
371 };
372 
374 {
375  std::auto_ptr<GPUImageDataBufferImpl> retval(new GPUImageDataBufferImpl);
376  retval->SetImage(volume);
377  return GPUImageDataBufferPtr(retval.release());
378 }
379 
381 {
382  std::auto_ptr<GPUImageLutBufferImpl> retval(new GPUImageLutBufferImpl);
383  retval->SetColorMap(lut);
384  return GPUImageLutBufferPtr(retval.release());
385 }
386 
387 template<class BUFFER, class DATA_PTR>
388 boost::shared_ptr<BUFFER> createGPUImageBuffer(DATA_PTR val);
389 
390 template<>
391 boost::shared_ptr<GPUImageDataBuffer> createGPUImageBuffer<GPUImageDataBuffer>(vtkImageDataPtr val);
392 template<>
393 boost::shared_ptr<GPUImageLutBuffer> createGPUImageBuffer<GPUImageLutBuffer>(vtkUnsignedCharArrayPtr val);
394 
395 template<>
397 {
398  return createGPUImageDataBuffer(val);
399 }
400 template<>
401 boost::shared_ptr<GPUImageLutBuffer> createGPUImageBuffer<GPUImageLutBuffer>(vtkUnsignedCharArrayPtr val)
402 {
403  return createGPUImageLutBuffer(val);
404 }
405 
406 
407 //---------------------------------------------------------
408 GPUImageBufferRepository* GPUImageBufferRepository::mInstance = NULL;
409 //---------------------------------------------------------
410 
411 template<class DATA_PTR, class BUFFER>
413 {
414 public:
415  typedef boost::shared_ptr<BUFFER> BufferPtr;
416  typedef boost::weak_ptr<BUFFER> BufferWeakPtr;
417 
418  struct BufferStore
419  {
420  BufferStore(DATA_PTR data, BufferPtr buffer) : mData(data), mBuffer(buffer) {}
421  DATA_PTR mData;
422  BufferPtr mBuffer;
423  };
424 public:
425 
426  BufferQueue() : mMaxBuffers(7)
427  {
428 
429  }
430  void setMaxBuffers(unsigned val)
431  {
432  mMaxBuffers = val;
433  }
434  void setName(const QString& name)
435  {
436  mName = name;
437  }
438 
443  BufferPtr get(DATA_PTR data)
444  {
445  // clear out deleted data
446  for (typename BufferMap::iterator iter=mRemovedData.begin(); iter!=mRemovedData.end(); )
447  {
448  if (!iter->second.lock())
449  {
450  typename BufferMap::iterator temp = iter;
451  ++iter;
452  mRemovedData.erase(temp);
453  }
454  else
455  {
456  ++iter;
457  }
458  }
459 
460  // reclaim weak pointer to buffer if it exists.
461  if (mRemovedData.count(data))
462  {
463  BufferPtr object = mRemovedData[data].lock();
464  if (object)
465  {
466  mData.push_front(BufferStore(data, object));
467  mRemovedData.erase(data);
468  }
469  }
470 
471  // move current data to front of buffer (i.e. increase importance)
472  BufferPtr retval;
473  for (typename std::list<BufferStore>::iterator iter=mData.begin(); iter!=mData.end(); ++iter)
474  {
475  if (iter->mData==data)
476  {
477  retval = iter->mBuffer; // retrieve data
478  mData.push_front(*iter); // push on queue front (most recent)
479  mData.erase(iter); // erase from old position
480  break;
481  }
482  }
483 
484  // create buffer if nonexistent
485  if (!retval)
486  {
487  retval = createGPUImageBuffer<BUFFER>(data);
488  mData.push_front(BufferStore(data, retval));
489  }
490 
491  // reduce repository size if too large.
492  while (mData.size()>mMaxBuffers)
493  {
494  mRemovedData[mData.back().mData] = mData.back().mBuffer;
495  mData.pop_back();;
496  }
497 
498  return retval;
499  }
500  int getMemoryUsage(int *textures)
501  {
502  int mem = 0;
503  if (textures)
504  {
505  *textures = mData.size();
506  }
507  for (typename std::list<BufferStore>::iterator iter=mData.begin(); iter!=mData.end(); ++iter)
508  {
509  mem += iter->mBuffer->getMemorySize();
510  }
511  return mem;
512  }
513 private:
514  typedef std::map<DATA_PTR, BufferWeakPtr> BufferMap;
515  BufferMap mRemovedData; // those buffers that are removed but still might live outside of the repo.
516  std::list<BufferStore> mData; // newest elems in front
517  unsigned mMaxBuffers;
518  QString mName; // debug
519 };
520 
522 {
523 public:
525  {
526  mVolumeBuffer.setName("Volume");
527  mLutBuffer.setName("Lut");
528 
529  mVolumeBuffer.setMaxBuffers(12);
530  mLutBuffer.setMaxBuffers(15);
531  }
534 };
535 
536 GPUImageBufferRepository::GPUImageBufferRepository()
537 {
538  mInternal = new GPUImageBufferRepositoryInternal();
539 }
540 
541 GPUImageBufferRepository::~GPUImageBufferRepository()
542 {
543  tearDown();
544 }
545 
547 {
548  tearDown();
549  mInternal = new GPUImageBufferRepositoryInternal();
550 }
551 
553 {
554  if (!mInstance)
555  {
556  mInstance = new GPUImageBufferRepository();
557  }
558  return mInstance;
559 }
560 
562 {
563  if(mInstance)
564  {
565  delete mInstance;
566  }
567  mInstance = NULL;
568 
569 }
570 
571 void GPUImageBufferRepository::tearDown()
572 {
573  delete mInternal;
574  mInternal = NULL;
575 }
576 
578 {
579  return mInternal->mVolumeBuffer.getMemoryUsage(textures);
580 }
581 
583 {
584  return mInternal->mVolumeBuffer.get(volume);
585 }
586 
588 {
589  return mInternal->mLutBuffer.get(lut);
590 }
591 
593 {
595 }
596 }//cx
597 
598 #else
599 
600 namespace cx
601 {
603 void GPUImageBufferRepository::tearDown() {;}
604 }//namespace cx
605 
606 #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:99
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:95
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:28
int getMemoryUsage(int *textures)
GPUImageBufferRepository * getGPUImageBufferRepository()
Namespace for all CustusX production code.