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