Документ взят из кэша поисковой машины. Адрес оригинального документа : http://astro.uni-altai.ru/~aw/stellarium/api/StelQGLRenderer_8hpp_source.html
Дата изменения: Unknown
Дата индексирования: Fri Feb 28 07:51:06 2014
Кодировка:
Stellarium: core/renderer/StelQGLRenderer.hpp Source File
Stellarium 0.12.3
StelQGLRenderer.hpp
1 /*
2  * Stellarium
3  * Copyright (C) 2012 Ferdinand Majerech
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  */
19 
20 #ifndef _STELQGLRENDERER_HPP_
21 #define _STELQGLRENDERER_HPP_
22 
23 
24 #include <QCache>
25 #include <QColor>
26 #include <QGLFunctions>
27 #include <QGraphicsView>
28 #include <QMap>
29 #include <QThread>
30 
31 #include "GenericVertexTypes.hpp"
32 #include "StelApp.hpp"
33 #include "StelGuiBase.hpp"
34 #include "StelUtils.hpp"
35 #include "StelProjectorType.hpp"
36 #include "StelRenderer.hpp"
37 #include "StelQGLArrayVertexBufferBackend.hpp"
38 #include "StelQGLIndexBuffer.hpp"
39 #include "StelQGLTextureBackend.hpp"
40 #include "StelQGLViewport.hpp"
41 #include "StelTextureCache.hpp"
42 #include "StelVertexBuffer.hpp"
43 
45 #define STELQGLRENDERER_MAX_TEXTURE_UNITS 64
46 
52 enum QGLRendererStatistics
53 {
54  ESTIMATED_TEXTURE_MEMORY = 0,
55  BATCHES = 1,
56  EMPTY_BATCHES = 2,
57  BATCHES_TOTAL = 3,
58  VERTICES = 4,
59  VERTICES_TOTAL = 5,
60  TRIANGLES = 6,
61  TRIANGLES_TOTAL = 7,
62  LINES = 8,
63  LINES_TOTAL = 9,
64  FRAMES = 10,
65  TEXT_DRAWS = 11,
66  RECT_DRAWS = 12,
67  GL_FPS = 13,
68  BATCH_PROJECTIONS_NONE_TOTAL = 14,
69  BATCH_PROJECTIONS_NONE = 15,
70  BATCH_PROJECTIONS_CPU_TOTAL = 16,
71  BATCH_PROJECTIONS_CPU = 17,
72  BATCH_PROJECTIONS_GPU_TOTAL = 18,
73  BATCH_PROJECTIONS_GPU = 19,
74  VERTEX_BUFFERS_CREATED = 20,
75  INDEX_BUFFERS_CREATED = 21,
76  TEXTURES_CREATED = 22,
77  SHADERS_CREATED = 23
78 };
79 
84 {
85 public:
91  StelQGLRenderer(QGraphicsView* const parent, const bool pvrSupported)
92  : StelRenderer()
93  , glContext(new QGLContext(QGLFormat(QGL::StencilBuffer |
94  QGL::DepthBuffer |
95  QGL::DoubleBuffer)))
96  , viewport(new StelQGLWidget(glContext, parent), parent)
97  , pvrSupported(pvrSupported)
98  , textureCache()
99  // Maximum bytes of text textures to store in the cache.
100  // Increased for the Pulsars plugin.
101  , textTextureCache(16777216)
102  , textBuffer(NULL)
103  , plainRectBuffer(NULL)
104  , texturedRectBuffer(NULL)
105  , previousFrameEndTime(-1.0)
106  , globalColor(Qt::white)
107  , depthTest(DepthTest_Disabled)
108  , stencilTest(StencilTest_Disabled)
109  , blendMode(BlendMode_None)
110  , culledFaces(CullFace_None)
111  , placeholderTexture(NULL)
112  , currentFontSet(false)
113  , textureUnitCount(-1)
114  , gl(glContext)
115  {
116  loaderThread = new QThread();
117  loaderThread->start(QThread::LowestPriority);
118  for(int t = 0; t < STELQGLRENDERER_MAX_TEXTURE_UNITS; ++t)
119  {
120  currentlyBoundTextures[t] = NULL;
121  }
122  }
123 
124  virtual ~StelQGLRenderer()
125  {
126  invariant();
127  loaderThread->quit();
128 
129  if(NULL != placeholderTexture)
130  {
131  delete placeholderTexture;
132  placeholderTexture = NULL;
133  }
134  textTextureCache.clear();
135  if(NULL != textBuffer)
136  {
137  delete textBuffer;
138  textBuffer = NULL;
139  }
140  if(NULL != texturedRectBuffer)
141  {
142  delete texturedRectBuffer;
143  texturedRectBuffer = NULL;
144  }
145  if(NULL != plainRectBuffer)
146  {
147  delete plainRectBuffer;
148  plainRectBuffer = NULL;
149  }
150 
151  // This causes crashes for some reason
152  // (perhaps it is already destroyed by QT? - didn't find that in the docs).
153  //
154  // delete glContext;
155 
156  glContext = NULL;
157 
158  loaderThread->wait();
159  delete loaderThread;
160  loaderThread = NULL;
161  }
162 
163  virtual bool init()
164  {
165  // Can't call makeGLContextCurrent() before initialization is complete.
166  glContext->makeCurrent();
167  textureUnitCount = getTextureUnitCountBackend();
168  glVendorString = QString(reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
169  qDebug() << "GL vendor is " << glVendorString;
170  glRendererString = QString(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
171  qDebug() << "GL renderer is " << glRendererString;
172  viewport.init(gl.hasOpenGLFeature(QGLFunctions::NPOTTextures),
174  initStatistics();
175  return true;
176  }
177 
178  virtual QImage screenshot()
179  {
180  invariant();
181  return viewport.screenshot();
182  }
183 
184  virtual void renderFrame(StelRenderClient& renderClient);
185 
186  virtual void viewportHasBeenResized(const QSize size)
187  {
188  invariant();
189  viewport.viewportHasBeenResized(size);
190  invariant();
191  }
192 
193  virtual QSize getViewportSize() const
194  {
195  invariant();
196  return viewport.getViewportSize();
197  }
198 
199  StelIndexBuffer* createIndexBuffer(const IndexType type)
200  {
201  statistics[INDEX_BUFFERS_CREATED] += 1.0;
202  return new StelQGLIndexBuffer(type);
203  }
204 
205  virtual void drawText(const TextParams& params);
206 
207  virtual void setFont(const QFont& font)
208  {
209  // The font is actually set only once drawing text.
210  currentFont = font;
211  currentFontSet = true;
212  }
213 
214  virtual void bindTextureBackend(StelTextureBackend* const textureBackend, const int textureUnit);
215 
216 
221  void ensureTextureNotBound(StelTextureBackend* const textureBackend)
222  {
223  for(int t = 0; t < STELQGLRENDERER_MAX_TEXTURE_UNITS; ++t)
224  {
225  if(currentlyBoundTextures[t] == textureBackend)
226  {
227  currentlyBoundTextures[t] = NULL;
228  gl.glActiveTexture(GL_TEXTURE0 + t);
229  glBindTexture(GL_TEXTURE_2D, 0);
230  }
231  }
232  }
233 
234  virtual void destroyTextureBackend(StelTextureBackend* const textureBackend);
235 
236  virtual void setGlobalColor(const Vec4f& color)
237  {
238  globalColor = color;
239  }
240 
241  virtual void setCulledFaces(const CullFace cullFace)
242  {
243  culledFaces = cullFace;
244  }
245 
246  virtual void setBlendMode(const BlendMode mode)
247  {
248  blendMode = mode;
249  }
250 
251  virtual void setDepthTest(const DepthTest test)
252  {
253  depthTest = test;
254  }
255 
256  virtual void clearDepthBuffer()
257  {
258  // glDepthMask enables writing to the depth buffer so we can clear it.
259  // This is a different API behavor from plain OpenGL. With OpenGL, the user
260  // must enable writing to the depth buffer to clear it.
261  // StelRenderer allows it to be cleared regardless of modes set by setDepthTest
262  // (which serves the same role as glDepthMask() in GL).
263  glDepthMask(GL_TRUE);
264  glClear(GL_DEPTH_BUFFER_BIT);
265  glDepthMask(GL_FALSE);
266  }
267 
268  virtual void setStencilTest(const StencilTest test)
269  {
270  stencilTest = test;
271  }
272 
273  virtual void clearStencilBuffer()
274  {
275  glClearStencil(0);
276  glClear(GL_STENCIL_BUFFER_BIT);
277  }
278 
279  virtual void swapBuffers()
280  {
281  invariant();
282  glContext->swapBuffers();
283  invariant();
284  }
285 
286  virtual void drawRect(const float x, const float y,
287  const float width, const float height,
288  const float angle = 0.0f);
289 
290  virtual void drawTexturedRect(const float x, const float y,
291  const float width, const float height,
292  const float angle = 0.0f);
293 
295  {
296  return statistics;
297  }
298 
300  virtual void makeGLContextCurrent()
301  {
302  invariant();
303  if(Q_UNLIKELY(QGLContext::currentContext() != glContext))
304  {
305  // makeCurrent does not check if the context is already current,
306  // so it can really kill performance
307  glContext->makeCurrent();
308  }
309  invariant();
310  }
311 
315  QGLContext* getGLContext()
316  {
317  return glContext;
318  }
319 
321  QGLFunctions& getGLFunctions()
322  {
323  return gl;
324  }
325 
327  QThread* getLoaderThread()
328  {
329  return loaderThread;
330  }
331 
333  const Vec4f& getGlobalColor() const
334  {
335  return globalColor;
336  }
337 
339  virtual bool areNonPowerOfTwoTexturesSupported() const = 0;
340 
342  QPaintEngine::Type qtPaintEngineType() const
343  {
344  return viewport.qtPaintEngineType();
345  }
346 
347 protected:
349  QString glVendorString;
350 
353 
356 
358  (const QString& filename, const TextureParams& params, const TextureLoadingMode loadingMode);
359 
361  (QImage& image, const TextureParams& params);
362 
364  (const void* data, const QSize size, const TextureDataFormat format,
365  const TextureParams& params);
366 
368  {
369  invariant();
370  return viewport.getViewportTextureBackend(this);
371  }
372 
374  virtual int getTextureUnitCountBackend() = 0;
375 
379 #ifndef NDEBUG
380  virtual void invariant() const
381  {
382  Q_ASSERT_X(NULL != glContext, Q_FUNC_INFO,
383  "An attempt to use a destroyed StelQGLRenderer.");
384  Q_ASSERT_X(glContext->isValid(), Q_FUNC_INFO, "The GL context is invalid");
385  }
386 #else
387  // This should guarantee this gets optimized away in release builds.
388  void invariant() const{}
389 #endif
390 
392  void setupGLState(StelProjector* projector)
393  {
394  // Instead of setting GL state when functions such as setDepthTest() or setCulledFaces()
395  // are called, we only set it before drawing and reset after drawing to avoid
396  // conflicts with e.g. Qt OpenGL backend, or any other GL code that might be running.
397  //
398  // Optimization note:
399  // Disabling GL state setup and reset improves performance by 3-8%
400  // (open source AMD driver), so that is the maximum speedup achievable
401  // by moving state setup into functions such as setBlendMode, setDepthTest, etc.
402 
403  // GL setup before drawing.
404  // Fix some problem when using Qt OpenGL2 engine
405  glStencilMask(0x11111111);
406 
407  // Moving this to drawVertexBufferBackend causes flicker issues
408  switch(blendMode)
409  {
410  case BlendMode_None:
411  glDisable(GL_BLEND);
412  break;
413  case BlendMode_Add:
414  glBlendFunc(GL_ONE, GL_ONE);
415  glEnable(GL_BLEND);
416  break;
417  case BlendMode_Alpha:
418  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
419  glEnable(GL_BLEND);
420  break;
421  case BlendMode_Multiply:
422  glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
423  glEnable(GL_BLEND);
424  break;
425  default:
426  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown blend mode");
427  }
428 
429  switch(depthTest)
430  {
431  case DepthTest_Disabled:
432  glDisable(GL_DEPTH_TEST);
433  break;
434  case DepthTest_ReadOnly:
435  glEnable(GL_DEPTH_TEST);
436  glDepthMask(GL_FALSE);
437  break;
438  case DepthTest_ReadWrite:
439  glEnable(GL_DEPTH_TEST);
440  glDepthMask(GL_TRUE);
441  break;
442  default:
443  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown depth test mode");
444  }
445 
446  switch(stencilTest)
447  {
448  case StencilTest_Disabled:
449  glDisable(GL_STENCIL_TEST);
450  break;
451  case StencilTest_Write_1:
452  glStencilFunc(GL_ALWAYS, 0x1, 0x1);
453  glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE);
454  glEnable(GL_STENCIL_TEST);
455  break;
456  case StencilTest_DrawIf_1:
457  glEnable(GL_STENCIL_TEST);
458  glStencilFunc(GL_EQUAL, 0x1, 0x1);
459  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
460  break;
461  default:
462  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown stencil test mode");
463  }
464 
465  switch(culledFaces)
466  {
467  case CullFace_None:
468  glDisable(GL_CULL_FACE);
469  break;
470  case CullFace_Front:
471  glEnable(GL_CULL_FACE);
472  glCullFace(GL_FRONT);
473  break;
474  case CullFace_Back:
475  glEnable(GL_CULL_FACE);
476  glCullFace(GL_BACK);
477  break;
478  default: Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown cull face type");
479  }
480 
481  glFrontFace(projector->flipFrontBackFace() ? GL_CW : GL_CCW);
482  }
483 
485  void restoreGLState(StelProjector* projector)
486  {
487  // More stuff could be restored here if there are any Qt drawing problems.
488  glFrontFace(projector->flipFrontBackFace() ? GL_CCW : GL_CW);
489  glCullFace(GL_BACK);
490  glDisable(GL_CULL_FACE);
491  glDisable(GL_DEPTH_TEST);
492  glDisable(GL_STENCIL_TEST);
493 
494  // Qt GL1 paint engine expects alpha blending to be enabled.
495  //
496  // If Qt fixes that, this might be removed.
497  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
498  glEnable(GL_BLEND);
499  }
500 
505  template<class VBufferBackend>
506  void updateDrawStatistics(VBufferBackend* vertexBuffer,
507  StelQGLIndexBuffer* indexBuffer)
508  {
509  statistics[BATCHES] += 1.0;
510  statistics[BATCHES_TOTAL] += 1.0;
511 
512  const int vertices =
513  (NULL == indexBuffer ? vertexBuffer->length() : indexBuffer->length());
514 
515  int triangles = 0;
516  int lines = 0;
517  switch(vertexBuffer->getPrimitiveType())
518  {
519  case PrimitiveType_Points:
520  break;
521  case PrimitiveType_Triangles:
522  triangles = vertices / 3;
523  break;
524  case PrimitiveType_TriangleStrip:
525  case PrimitiveType_TriangleFan:
526  triangles = vertices >= 3 ? vertices - 2 : 0;
527  break;
528  case PrimitiveType_Lines:
529  lines = vertices / 2;
530  break;
531  case PrimitiveType_LineStrip:
532  lines = vertices >= 2 ? vertices - 1 : 0;
533  break;
534  case PrimitiveType_LineLoop:
535  lines = vertices;
536  break;
537  default:
538  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown graphics primitive type");
539  }
540 
541  statistics[VERTICES] += vertices;
542  statistics[VERTICES_TOTAL] += vertices;
543  statistics[TRIANGLES] += triangles;
544  statistics[TRIANGLES_TOTAL] += triangles;
545  statistics[LINES] += lines;
546  statistics[LINES_TOTAL] += lines;
547  }
548 
549 private:
553  struct FrameTimeQueue
554  {
555  public:
557  FrameTimeQueue()
558  : nextFrame(0)
559  {
560  memset(frames, '\0', FRAME_CAPACITY * sizeof(long double));
561  }
562 
566  void update()
567  {
568  frames[nextFrame++] = StelUtils::secondsSinceStart();
569  nextFrame = nextFrame % FRAME_CAPACITY;
570  }
571 
573  double getFPS()
574  {
575  const long double newest = frames[(nextFrame - 1 + 256) % FRAME_CAPACITY];
576  long double oldest = frames[nextFrame];
577  Q_ASSERT_X(newest >= oldest, Q_FUNC_INFO,
578  "Oldest frame time is more recent than newest");
579  int tooOldCount = 0;
580  // Don't compute FPS from more than one second of frames.
581  while(newest - oldest > 1.0 && tooOldCount < FRAME_CAPACITY - 1)
582  {
583  ++tooOldCount;
584  oldest = frames[(nextFrame + tooOldCount) % FRAME_CAPACITY];
585  }
586  return (FRAME_CAPACITY - tooOldCount) / (newest - oldest);
587  }
588 
589  private:
591  int nextFrame;
592 
594  static const int FRAME_CAPACITY = 128;
595 
597  long double frames[FRAME_CAPACITY];
598  };
599 
601  QGLContext* glContext;
602 
604  StelQGLViewport viewport;
605 
607  bool pvrSupported;
608 
610  QThread* loaderThread;
611 
617 
621  QCache<QByteArray, StelQGLTextureBackend> textTextureCache;
622 
624  struct TexturedVertex
625  {
626  Vec2f position;
627  Vec2f texCoord;
628  TexturedVertex(const Vec2f& position, const Vec2f& texCoord)
629  : position(position), texCoord(texCoord){}
630 
631  VERTEX_ATTRIBUTES(Vec2f Position, Vec2f TexCoord);
632  };
633 
636 
638  StelVertexBuffer<VertexP2>* plainRectBuffer;
639 
641  StelVertexBuffer<TexturedVertex>* texturedRectBuffer;
642 
646  double previousFrameEndTime;
647 
655  Vec4f globalColor;
656 
658  DepthTest depthTest;
659 
661  StencilTest stencilTest;
662 
664  BlendMode blendMode;
665 
667  CullFace culledFaces;
668 
670  StelQGLTextureBackend* placeholderTexture;
671 
675  StelQGLTextureBackend* currentlyBoundTextures[STELQGLRENDERER_MAX_TEXTURE_UNITS];
676 
678  QFont currentFont;
679 
682  bool currentFontSet;
683 
685  int textureUnitCount;
686 
688  FrameTimeQueue recentFrames;
689 
691  void drawWindow(StelViewportEffect* const effect);
692 
694  void initStatistics()
695  {
696  // NOTE: Order of statistics added here _must_ match the order
697  // of values of the QGLRendererStatistics enum.
698  statistics.addStatistic("estimated_texture_memory");
699 
700  // Draw statistics
701  statistics.addStatistic("batches", StatisticSwapMode_SetToZero);
702  statistics.addStatistic("empty_batches", StatisticSwapMode_SetToZero);
703  statistics.addStatistic("batches_total");
704  statistics.addStatistic("vertices", StatisticSwapMode_SetToZero);
705  statistics.addStatistic("vertices_total");
706  statistics.addStatistic("triangles", StatisticSwapMode_SetToZero);
707  statistics.addStatistic("triangles_total");
708  statistics.addStatistic("lines", StatisticSwapMode_SetToZero);
709  statistics.addStatistic("lines_total");
710  statistics.addStatistic("frames");
711  statistics.addStatistic("text_draws", StatisticSwapMode_SetToZero);
712  statistics.addStatistic("rect_draws", StatisticSwapMode_SetToZero);
713  statistics.addStatistic("gl_fps");
714 
715  // Projection statistics
716  statistics.addStatistic("batch_projections_none_total");
717  statistics.addStatistic("batch_projections_none", StatisticSwapMode_SetToZero);
718  statistics.addStatistic("batch_projections_cpu_total");
719  statistics.addStatistic("batch_projections_cpu", StatisticSwapMode_SetToZero);
720  statistics.addStatistic("batch_projections_gpu_total");
721  statistics.addStatistic("batch_projections_gpu", StatisticSwapMode_SetToZero);
722 
723  // Creation of Renderer objects
724  statistics.addStatistic("vertex_buffers_created");
725  statistics.addStatistic("index_buffers_created");
726  statistics.addStatistic("textures_created");
727  statistics.addStatistic("shaders_created");
728  }
729 
731  void clearFrameStatistics()
732  {
733  statistics.swap();
734  statistics[FRAMES] += 1.0;
735  statistics[GL_FPS] = recentFrames.getFPS();
736  }
737 
747  void drawTextGravityHelper
748  (const TextParams& params, QPainter& painter, const int baseX, const int baseY, StelProjectorP projector);
749 
751  StelQGLTextureBackend* getPlaceholderTexture(bool debug=false)
752  {
753  if(NULL != placeholderTexture) {return placeholderTexture;}
754 
755  int color = 0;
756  if (debug)
757  color = 0x00FFFF00;
758 
759  const int placeholderSize = 512;
760  const int cellSize = 16;
761  QImage image(placeholderSize, placeholderSize, QImage::Format_RGB888);
762  for(int y = 0; y < placeholderSize; ++y)
763  {
764  for (int x = 0; x < placeholderSize; x++)
765  {
766  image.setPixel(x, y, (x / cellSize) % 2 == (y / cellSize) % 2 ? 0 : color);
767  }
768  }
769  placeholderTexture =
770  StelQGLTextureBackend::constructFromImage(this, QString(), TextureParams(), image);
771 
772  return placeholderTexture;
773  }
774 
789  void drawRectInternal(const bool textured, const float x, const float y,
790  const float width, const float height, const float angle);
791 
792  // Must be down due to initializer list order.
793 protected:
795  QGLFunctions gl;
796 };
797 
798 #endif // _STELQGLRENDERER_HPP_