CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxSharedOpenGLContext.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 //needed on windows to where <windows.h> is included
13 #ifndef NOMINMAX
14 #define NOMINMAX
15 #endif
16 
17 #include "cxSharedOpenGLContext.h"
18 
19 #include <vtkOpenGLRenderWindow.h>
20 #include <vtkNew.h>
21 #include <vtkTextureObject.h>
22 #include <vtkOpenGLBufferObject.h>
23 #include <vtkImageData.h>
24 #include <vtkSetGet.h>
25 #include <vtkFloatArray.h>
26 #include <vtkPixelBufferObject.h>
27 #include <vtkUnsignedCharArray.h>
28 #include <vtkTextureUnitManager.h>
29 
30 #include "cxGLHelpers.h"
31 #include "cxLogger.h"
32 #include "cxImage.h"
33 #include "cxUtilHelpers.h"
34 #include "cxSettings.h"
35 
36 namespace cx
37 {
38 
40 {
41  bool valid = true;
42 
43  if(!opengl_renderwindow)
44  {
45  valid=false;
46  }
47 
48  if(print)
49  {
50  CX_LOG_DEBUG() << "\n==== START SharedContext ====";
51  //NB! opengl_renderwindow->SupportsOpenGL() creates a new context even if you have one, and this seg fault on render in vtkCocoaRenderWindow::CreateGLContext().
52  //CX_LOG_DEBUG() << "SupportsOpenGL: " << opengl_renderwindow->SupportsOpenGL();
53  CX_LOG_DEBUG() << "IsDrawable: " << opengl_renderwindow->IsDrawable();
54  CX_LOG_DEBUG() << "Context support for open gl core 3.2: " << (vtkOpenGLRenderWindow::GetContextSupportsOpenGL32() ? "true" : "false");
55  CX_LOG_DEBUG() << "Context was created at: " << opengl_renderwindow->GetContextCreationTime();
56  const char *renderlib = vtkRenderWindow::GetRenderLibrary();
57  CX_LOG_DEBUG() << "GetRenderLibrary: " << ((renderlib!=0) ? std::string(renderlib) : "NOT FOUND");
58  CX_LOG_DEBUG() << "vtkOpenGLRenderWindow:";
59  CX_LOG_DEBUG() << "==== END SharedContext ====\n";
60  }
61 
63  return valid;
64 }
65 
66 SharedOpenGLContext::SharedOpenGLContext(vtkOpenGLRenderWindowPtr sharedContext) :
67  mContext(sharedContext)
68 {
69 }
70 
72 {
73 }
74 
75 bool SharedOpenGLContext::hasUploadedImage(QString image_uid) const
76 {
77  return m3DTextureObjects.count(image_uid);
78 }
79 
81 {
82  vtkTextureObjectPtr retval;
83 
84  if(hasUploadedImage(image_uid))
85  {
86  retval = m3DTextureObjects.at(image_uid).first;
87  }
88 // else
89 // {
90 // CX_LOG_ERROR() << "get3DTexture failed, seems 3D texture is not uploaded";
91 // }
92 
93  return retval;
94 }
95 
97 {
98  bool success = false;
99  vtkTextureObjectPtr texture = this->get3DTextureForImage(image_uid);
100  std::map<QString, std::pair<vtkTextureObjectPtr, unsigned long> >::iterator it = m3DTextureObjects.find(image_uid);
101 
102  if(it != m3DTextureObjects.end())
103  {
104  m3DTextureObjects.erase(it);
105  texture->ReleaseGraphicsResources(mContext);
106  success = true;
107  }
108 
109  return success;
110 }
111 
113 {
114  vtkPixelBufferObjectPtr pixelBuffer;
115  vtkImageDataPtr imageData = vtkImageDataPtr::New();
116  vtkTextureObjectPtr texture = this->get3DTextureForImage(image_uid);
117 
118  if(texture)
119  {
120  pixelBuffer = texture->Download();
121  int dataType = texture->GetVTKDataType();
122  unsigned width = texture->GetWidth();
123  unsigned height = texture->GetHeight();
124  unsigned depth = texture->GetDepth();
125  unsigned dims[3] = {width, height, depth};
126  int numComps = texture->GetComponents();
127  vtkIdType increments[3] = {0, 0, 0};
128  imageData->SetExtent(0, width-1, 0, height-1, 0, depth-1);
129  //imageData->SetSpacing(1, 1, 1);
130  imageData->AllocateScalars(dataType, numComps);
131  void* data = imageData->GetScalarPointer();
132  pixelBuffer->Download3D(dataType, data, dims, numComps, increments);
133  }
134  else
135  {
136  CX_LOG_ERROR() << "SharedOpenGLContext::downloadImageFromTextureBuffer failed";
137  }
138 
139  return imageData;
140 }
141 
143 {
144  mContext->MakeCurrent();
145  return mContext->IsCurrent();
146 }
147 
149 {
150  int texture_units_in_use = 0;
151  texture_units_in_use += m3DTextureObjects.size();
152  texture_units_in_use += m1DTextureObjects.size();
153  return texture_units_in_use;
154 }
155 
157 {
158  report_gl_error();
159  bool success = false;
160 
161  if(!image)
162  {
163  CX_LOG_ERROR() << "Cannot upload en empty image as a 3D texture";
164  return success;
165  }
166 
167  unsigned long new_modified_time = image->getBaseVtkImageData()->GetMTime();
168  std::map<QString, std::pair<vtkTextureObjectPtr, unsigned long> >::iterator it = m3DTextureObjects.find(image->getUid());
169  bool uploaded_but_modified = (it != m3DTextureObjects.end() && (it->second.second != new_modified_time) );
170  bool not_uploaded = it == m3DTextureObjects.end();
171  bool uploade_and_not_modified = (it != m3DTextureObjects.end() && (it->second.second == new_modified_time) );
172 
173  if( uploaded_but_modified || not_uploaded)
174  {
175  //upload new data to gpu
176  vtkImageDataPtr vtkImageData = image->getBaseVtkImageData();
177  int* dims = vtkImageData->GetDimensions();
178  int dataType = vtkImageData->GetScalarType();
179  int numComps = vtkImageData->GetNumberOfScalarComponents();
180  int coordinate[3] = {dims[0]/2, dims[1]/2, dims[2]/2};
181  void* data = vtkImageData->GetScalarPointer();
182  //CX_LOG_DEBUG() << "dims: " << dims[0] << " " << dims[1] << " " << dims[2] << " " << " scalarType: " << vtkImageData->GetScalarTypeAsString() << " numComps: " << numComps;
183  //CX_LOG_DEBUG() << "data in the middle 1 " << vtkImageData->GetScalarComponentAsFloat(dims[0]/2, dims[1]/2, dims[2]/2, 0);
184  //CX_LOG_DEBUG() << "data in the middle 2 " << (int)((unsigned char*)vtkImageData->GetScalarPointer(coordinate))[0];
185 
186  //int size = dims[0]*dims[1]*dims[2]*numComps;
187  //CX_LOG_DEBUG() << "data in the middle 3 " << (int)((reinterpret_cast<unsigned char*>(data)[size/2+1]));
188  //for(int i=0; i<size; i=i+150)
189  // std::cout << (int)((reinterpret_cast<unsigned char*>(data)[i]));
190  //std::cout << std::endl;
191 
192  vtkTextureObjectPtr texture_object = this->get3DTextureForImage(image->getUid());
193  if(!texture_object) //not uploaded
194  {
195  texture_object = vtkTextureObjectPtr::New();
196  //CX_LOG_DEBUG() << "create new texture_object";
197  }
198 
199  success = this->create3DTextureObject(texture_object, dims[0], dims[1], dims[2], dataType, numComps, data, mContext);
200  m3DTextureObjects[image->getUid()] = std::make_pair(texture_object, vtkImageData->GetMTime());
201  }
202  else if(uploade_and_not_modified)
203  {
204  //do nothing
205  success = true;
206  }
207 
208  /*
209  report_gl_error();
210  vtkTextureObjectPtr texture_object;
211 
212  //TODO refactor to be similar to uploadLUT
213  if(!m3DTextureObjects.count(image->getUid()))
214  {
215  vtkImageDataPtr vtkImageData = image->getBaseVtkImageData();
216  int* dims = vtkImageData->GetDimensions();
217  int dataType = vtkImageData->GetScalarType();
218  int numComps = vtkImageData->GetNumberOfScalarComponents();
219  int coordinate[3] = {dims[0]/2, dims[1]/2, dims[2]/2};
220  void* data = vtkImageData->GetScalarPointer();
221  //CX_LOG_DEBUG() << "dims: " << dims[0] << " " << dims[1] << " " << dims[2] << " " << " scalarType: " << vtkImageData->GetScalarTypeAsString() << " numComps: " << numComps;
222  //CX_LOG_DEBUG() << "data in the middle 1 " << vtkImageData->GetScalarComponentAsFloat(dims[0]/2, dims[1]/2, dims[2]/2, 0);
223  //CX_LOG_DEBUG() << "data in the middle 2 " << (int)((unsigned char*)vtkImageData->GetScalarPointer(coordinate))[0];
224 
225  //int size = dims[0]*dims[1]*dims[2]*numComps;
226  //CX_LOG_DEBUG() << "data in the middle 3 " << (int)((reinterpret_cast<unsigned char*>(data)[size/2+1]));
227  //for(int i=0; i<size; i=i+150)
228  // std::cout << (int)((reinterpret_cast<unsigned char*>(data)[i]));
229  //std::cout << std::endl;
230 
231  texture_object = this->create3DTextureObject(dims[0], dims[1], dims[2], dataType, numComps, data, mContext);
232  m3DTextureObjects[image->getUid()] = std::make_pair(texture_object, vtkImageData->GetMTime());
233  //m3DTextureObjects_new[image->getUid()] = std::pair<vtkTextureObjectPtr, unsigned long int>(texture_object, texture_object->GetMTime());
234  success = true;
235  }
236  else
237  {
238  //TODO Update image data?
239  //CX_LOG_WARNING() << "Image already exists as a texture, it is not uploaded again. This might be an error if you have changed the images data since first upload.";
240  }
241  */
242  report_gl_error();
243  return success;
244 }
245 
247 {
248  report_gl_error();
249  bool success = false;
250 
251  if(lutTable->GetSize() == 0)
252  {
253  CX_LOG_ERROR() << "Cannot upload en empty LUT table as a 1D texture";
254  return success;
255  }
256 
257  unsigned long new_modified_time = lutTable->GetMTime();
258  std::map<QString, std::pair<vtkTextureObjectPtr, unsigned long> >::iterator it = m1DTextureObjects.find(imageUid);
259  bool uploaded_but_modified = (it != m1DTextureObjects.end() && (it->second.second != new_modified_time) );
260  bool not_uploaded = it == m1DTextureObjects.end();
261  bool uploade_and_not_modified = (it != m1DTextureObjects.end() && (it->second.second == new_modified_time) );
262 
263  if( uploaded_but_modified || not_uploaded)
264  {
265  //upload new data to gpu
266  int lutSize = lutTable->GetNumberOfTuples();
267  int lutDataSize = lutSize * lutTable->GetNumberOfComponents();
268 
269  //LUT table contains values between 0-255
270  //we need normalized values between 0-1
271  std::vector<float> normalizeLUT;
272  normalizeLUT.resize(lutDataSize);
273  unsigned char* ptr = lutTable->GetPointer(0);
274 
275  for (int i = 0; i < normalizeLUT.size(); ++i)
276  {
277  normalizeLUT[i] = ((float) *ptr) / 255.0;
278  ++ptr;
279  }
280 
281  unsigned int width = lutSize;
282  int dataType = VTK_FLOAT;
283  int numComps = lutTable->GetNumberOfComponents();
284  void *data = &(*normalizeLUT.begin());
285 
286 
287  vtkTextureObjectPtr texture_object = this->get1DTextureForLUT(imageUid);
288  if(!texture_object.GetPointer()) //not uploaded
289  {
290  //CX_LOG_DEBUG() << "lut for " << imageUid << " not_uploaded";
291  texture_object = vtkTextureObjectPtr::New();
292  //CX_LOG_DEBUG() << "create new texture_object";
293  }
294  //CX_LOG_DEBUG() << "lut for " << imageUid << " uploaded_but_modified";
295  success = this->create1DTextureObject(texture_object, width, dataType, numComps, data, mContext);
296  //CX_LOG_DEBUG() << "1D texture, handlet: " << texture_object->GetHandle() << " width: " << width << " numComps: " << numComps;
297  m1DTextureObjects[imageUid] = std::make_pair(texture_object, lutTable->GetMTime());
298  }
299  else if(uploade_and_not_modified)
300  {
301  //do nothing
302  success = true;
303  //TODO these never happens for some reason, someone is setting LUT as modified
304  //CX_LOG_DEBUG() << "----------------- lut for " << imageUid << " uploade_and_not_modified";
305  }
306  report_gl_error();
307 
308  return success;
309 }
310 
311 bool SharedOpenGLContext::hasUploadedLUT(QString image_uid) const
312 {
313  return m1DTextureObjects.count(image_uid);
314 }
315 
317 {
318  vtkTextureObjectPtr retval;
319  if(hasUploadedLUT(image_uid))
320  {
321  retval = m1DTextureObjects.at(image_uid).first;
322  }
323  return retval;
324 }
325 
327 {
328  bool success = false;
329  vtkTextureObjectPtr texture = this->get1DTextureForLUT(image_uid);
330  std::map<QString, std::pair<vtkTextureObjectPtr, unsigned long> >::iterator it = m1DTextureObjects.find(image_uid);
331 
332  if(it != m1DTextureObjects.end())
333  {
334  m1DTextureObjects.erase(it);
335  texture->ReleaseGraphicsResources(mContext);
336  success = true;
337  }
338 
339  return success;
340 
341 }
342 
344 {
345  bool success = false;
346 
347  if(uid.isEmpty())
348  {
349  return success;
350  }
351 
352  if(!texture_coordinates)
353  {
354  return success;
355  }
356 
357  if(texture_coordinates->GetNumberOfTuples() < 1)
358  {
359  CX_LOG_ERROR() << "Cannot upload en empty array as 3D texture coordinates";
360  return success;
361  }
362 
363  int numberOfLines = texture_coordinates->GetNumberOfTuples();
364  int numberOfComponentsLine = texture_coordinates->GetNumberOfComponents();
365  vtkOpenGLBufferObjectPtr buffer = allocateAndUploadArrayBuffer(uid, numberOfLines, numberOfComponentsLine, texture_coordinates->GetPointer(0));
366 
367  if(buffer)
368  {
369  mTextureCoordinateBuffers[uid] = buffer;
370  success = true;
371  }
372 
373  return success;
374 }
375 
377 {
378  return mTextureCoordinateBuffers.count(uid);
379 }
380 
382 {
384 
386  {
387  retval = mTextureCoordinateBuffers.at(uid);
388  }
389  else
390  {
391  CX_LOG_ERROR() << "getTextureCoordinates failed";
392  }
393 
394  return retval;
395 }
396 
397 bool SharedOpenGLContext::create3DTextureObject(vtkTextureObjectPtr texture_object, unsigned int width, unsigned int height, unsigned int depth, int dataType, int numComps, void *data, vtkOpenGLRenderWindowPtr opengl_renderwindow) const
398 {
399 
400  if(!this->makeCurrent())
401  {
402  CX_LOG_ERROR() << "Could not make current for 3D texture";
403  return false;
404  }
405 
406  texture_object->SetContext(opengl_renderwindow);
407  //opengl_renderwindow->ActivateTexture(texture_object);
408 
409  if(texture_object->Create3DFromRaw(width, height, depth, numComps, dataType, data))
410  {
411  report_gl_error();
412  //6403 == GL_RED 0x1903
413  //6407 == GL_RGB 0x1907
414  //6408 == GL_RGBA 0x1908
415  //CX_LOG_DEBUG() << texture_object->GetFormat(dataType, numComps, true);
416  texture_object->Activate();
417  report_gl_error();
418  texture_object->SetWrapS(vtkTextureObject::ClampToBorder);
419  texture_object->SetWrapT(vtkTextureObject::ClampToBorder);
420  texture_object->SetWrapR(vtkTextureObject::ClampToBorder);
421  if(this->useLinearInterpolation())
422  {
423  texture_object->SetMagnificationFilter(vtkTextureObject::Linear);
424  texture_object->SetMinificationFilter(vtkTextureObject::Linear);
425  }
426  else
427  {
428  texture_object->SetMagnificationFilter(vtkTextureObject::Nearest);
429  texture_object->SetMinificationFilter(vtkTextureObject::Nearest);
430  }
431  texture_object->SendParameters();
432  texture_object->Deactivate();
433  report_gl_error();
434  }
435  else
436  {
437  CX_LOG_ERROR() << "Error creating 3D texture";
438  }
439 
440  return true;
441 }
442 
443 bool SharedOpenGLContext::useLinearInterpolation() const
444 {
445  return settings()->value("View2D/useLinearInterpolationIn2DRendering").toBool();
446 }
447 
448 bool SharedOpenGLContext::create1DTextureObject(vtkTextureObjectPtr texture_object, unsigned int width, int dataType, int numComps, void *data, vtkOpenGLRenderWindowPtr opengl_renderwindow) const
449 {
450  if(!this->makeCurrent())
451  {
452  CX_LOG_ERROR() << "Could not make current for 1D texture";
453  return false;
454  }
455 
456  texture_object->SetContext(opengl_renderwindow);
457 
458  if(texture_object->Create1DFromRaw(width, numComps, dataType, data))
459  {
460  report_gl_error();
461  //6403 == GL_RED 0x1903
462  //6407 == GL_RGB 0x1907
463  //6408 == GL_RGBA 0x1908
464  texture_object->Activate();
465  report_gl_error();
466  texture_object->SetWrapS(vtkTextureObject::ClampToEdge);
467  texture_object->SetMagnificationFilter(vtkTextureObject::Linear);
468  texture_object->SetMinificationFilter(vtkTextureObject::Linear);
469  texture_object->SendParameters();
470  //CX_LOG_DEBUG() << "Texture unit: " << texture_object->GetTextureUnit();
471  //CX_LOG_DEBUG() << "Created. Handle: " << texture_object->GetHandle() << " target: " << texture_object->GetTarget() << " context: ";
472  //texture_object->GetContext()->PrintSelf(std::cout, vtkIndent(9));
473  texture_object->Deactivate();
474  report_gl_error();
475  }
476  else
477  {
478  CX_LOG_ERROR() << "Error creating 1D texture";
479  }
480 
481  return true;
482 }
483 
484 vtkOpenGLBufferObjectPtr SharedOpenGLContext::allocateAndUploadArrayBuffer(QString uid, int numberOfLines, int numberOfComponentsLine, const float *data) const
485 {
486  if(!data)
487  {
488  CX_LOG_WARNING() << "NO DATA!";
489  }
490 
491  vtkOpenGLBufferObjectPtr buffer_object;;
492  //buffer_object->DebugOn();
493 
494  if(!this->makeCurrent())
495  {
496  CX_LOG_ERROR() << "Could not make current for ArraryBuffer";
497  return buffer_object;
498  }
499 
500  //CX_LOG_DEBUG() << "ALLOCATING BUFFER";
502  {
503  buffer_object = getTextureCoordinates(uid);
504  }
505  else
506  {
507  buffer_object = vtkOpenGLBufferObjectPtr::New();
508  }
509 
510  buffer_object->GenerateBuffer(vtkOpenGLBufferObject::ArrayBuffer);
511 
512  if(buffer_object->Bind())
513  {
514  //CX_LOG_DEBUG() << "UPLOADING BUFFER: uid:" << uid << " handle: " << buffer_object->GetHandle();
515  if(!buffer_object->Upload(
516  data,
517  numberOfLines*numberOfComponentsLine, //how many floats to upload! (aka number of floats in the vector)
518  vtkOpenGLBufferObject::ArrayBuffer
519  ))
520  {
521  CX_LOG_ERROR() << "Error uploading buffer object data.";
522  }
523 
524  report_gl_error();
525  }
526  else
527  {
528  CX_LOG_ERROR() << "Buffer object could not bind";
529  }
530 
531  report_gl_error();
532  return buffer_object;
533 }
534 
535 
536 }//cx
537 
vtkSmartPointer< class vtkTextureObject > vtkTextureObjectPtr
bool hasUploadedLUT(QString image_uid) const
bool hasUploadedImage(QString image_uid) const
bool uploadImage(ImagePtr image)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
vtkSmartPointer< class vtkFloatArray > vtkFloatArrayPtr
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:66
bool upload3DTextureCoordinates(QString uid, vtkFloatArrayPtr texture_coordinates)
vtkSmartPointer< class vtkUnsignedCharArray > vtkUnsignedCharArrayPtr
vtkImageDataPtr downloadImageFromTextureBuffer(QString image_uid)
vtkTextureObjectPtr get3DTextureForImage(QString image_uid) const
vtkOpenGLBufferObjectPtr getTextureCoordinates(QString uid) const
vtkSmartPointer< class vtkOpenGLBufferObject > vtkOpenGLBufferObjectPtr
bool delete3DTextureForImage(QString image_uid)
vtkSmartPointer< class vtkOpenGLRenderWindow > vtkOpenGLRenderWindowPtr
#define CX_LOG_ERROR
Definition: cxLogger.h:99
bool uploadLUT(QString imageUid, vtkUnsignedCharArrayPtr lutTable)
vtkSmartPointer< class vtkPixelBufferObject > vtkPixelBufferObjectPtr
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:21
bool delete1DTextureForLUT(QString image_uid)
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
#define CX_LOG_WARNING
Definition: cxLogger.h:98
bool hasUploadedTextureCoordinates(QString uid) const
static bool isValid(vtkOpenGLRenderWindowPtr opengl_renderwindow, bool print=false)
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
void print(QString header, QRect r)
vtkTextureObjectPtr get1DTextureForLUT(QString image_uid) const
#define report_gl_error()
Definition: cxGLHelpers.h:28
Namespace for all CustusX production code.