OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4DrawGLCtx.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 
17 /* OpenGL implementation of NewGfx, the context */
18 
19 #include "C4Include.h"
21 #include "graphics/C4DrawGL.h"
22 
23 #include "platform/C4App.h"
24 #include "platform/C4Window.h"
25 
26 #ifndef USE_CONSOLE
27 
28 static const int REQUESTED_GL_CTX_MAJOR = 3;
29 static const int REQUESTED_GL_CTX_MINOR = 2;
30 
31 std::list<CStdGLCtx*> CStdGLCtx::contexts;
32 
34 {
35  pGL->pCurrCtx = this;
36  // set some default states
37  glDisable(GL_DEPTH_TEST);
38  glDepthFunc(GL_LESS);
39  glDisable(GL_CULL_FACE);
40  glEnable(GL_BLEND);
41  // Delete pending VAOs
42  std::vector<GLuint> toBeDeleted;
43  if (!VAOsToBeDeleted.empty())
44  {
45  for (unsigned int i : VAOsToBeDeleted)
46  {
47  if (i < hVAOs.size() && hVAOs[i] != 0)
48  {
49  toBeDeleted.push_back(hVAOs[i]);
50  hVAOs[i] = 0;
51  }
52  }
53 
54  glDeleteVertexArrays(toBeDeleted.size(), &toBeDeleted[0]);
55  VAOsToBeDeleted.clear();
56  }
57 }
58 
59 #ifdef USE_WGL
60 
61 #include <GL/wglew.h>
62 
63 static PIXELFORMATDESCRIPTOR pfd; // desired pixel format
64 static HGLRC hrc = nullptr;
65 
66 // Enumerate available pixel formats. Choose the best pixel format in
67 // terms of color and depth buffer bits and then return all formats with
68 // different multisampling settings. If there are more then one, then choose
69 // the one with highest depth buffer size and lowest stencil and auxiliary
70 // buffer sizes since we don't use them in Clonk.
71 static std::vector<int> EnumeratePixelFormats(HDC hdc)
72 {
73  std::vector<int> result;
74  if(!wglGetPixelFormatAttribivARB) return result;
75 
76  int n_formats;
77  int attributes = WGL_NUMBER_PIXEL_FORMATS_ARB;
78  if(!wglGetPixelFormatAttribivARB(hdc, 0, 0, 1, &attributes, &n_formats)) return result;
79 
80  for(int i = 1; i < n_formats+1; ++i)
81  {
82  int new_attributes[] = { WGL_DRAW_TO_WINDOW_ARB, WGL_SUPPORT_OPENGL_ARB, WGL_DOUBLE_BUFFER_ARB, WGL_COLOR_BITS_ARB, WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB, WGL_AUX_BUFFERS_ARB, WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB };
83  const unsigned int nnew_attributes = sizeof(new_attributes)/sizeof(int);
84 
85  int new_results[nnew_attributes];
86  if(!wglGetPixelFormatAttribivARB(hdc, i, 0, nnew_attributes, new_attributes, new_results)) continue;
87  if(!new_results[0] || !new_results[1] || !new_results[2]) continue;
88  if(new_results[3] < 16 || new_results[4] < 16) continue; // require at least 16 bits per pixel in color and depth
89 
90  // For no MS we do find a pixel format with depth 32 on my (ck's) computer,
91  // however, when choosing it then texturing does not work anymore. I am not
92  // exactly sure what the cause of that is, so let's not choose that one for now.
93  if(new_results[4] > 24) continue;
94 
95  // Multisampling with just one sample is equivalent to no-multisampling,
96  // so don't include that in the result.
97  if(new_results[7] == 1 && new_results[8] == 1)
98  continue;
99 
100  if(result.empty())
101  {
102  result.push_back(i);
103  }
104  else
105  {
106  int old_attributes[] = { WGL_COLOR_BITS_ARB };
107  const unsigned int nold_attributes = sizeof(old_attributes)/sizeof(int);
108  int old_results[nold_attributes];
109 
110  if(!wglGetPixelFormatAttribivARB(hdc, result[0], 0, nold_attributes, old_attributes, old_results)) continue;
111 
112  if(new_results[3] > old_results[0])
113  {
114  result.clear();
115  result.push_back(i);
116  }
117  else if(new_results[3] == old_results[0])
118  {
119  unsigned int j;
120  for(j = 0; j < result.size(); ++j)
121  {
122  int equiv_attributes[] = { WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB, WGL_AUX_BUFFERS_ARB, WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB };
123  const unsigned int nequiv_attributes = sizeof(equiv_attributes)/sizeof(int);
124  int equiv_results[nequiv_attributes];
125  if(!wglGetPixelFormatAttribivARB(hdc, result[j], 0, nequiv_attributes, equiv_attributes, equiv_results)) continue;
126 
127  if(new_results[7] == equiv_results[3] && new_results[8] == equiv_results[4])
128  {
129  if(new_results[4] > equiv_results[0] || (new_results[4] == equiv_results[0] && (new_results[5] < equiv_results[1] || (new_results[5] == equiv_results[1] && new_results[6] < equiv_results[2]))))
130  result[j] = i;
131  break;
132  }
133  }
134 
135  if(j == result.size()) result.push_back(i);
136  }
137  }
138  }
139 
140  return result;
141 }
142 
143 static int GetPixelFormatForMS(HDC hDC, int samples)
144 {
145  std::vector<int> vec = EnumeratePixelFormats(hDC);
146  for(int i : vec)
147  {
148  int attributes[] = { WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB };
149  const unsigned int n_attributes = 2;
150  int results[2];
151  if(!wglGetPixelFormatAttribivARB(hDC, i, 0, n_attributes, attributes, results)) continue;
152 
153  if( (samples == 0 && results[0] == 0) ||
154  (samples > 0 && results[0] == 1 && results[1] == samples))
155  {
156  return i;
157  }
158  }
159 
160  return 0;
161 }
162 
163 class WinAPIError : public std::runtime_error
164 {
165 public:
166  typedef DWORD error_code;
167 
168  WinAPIError() : WinAPIError(GetLastError()) {}
169  WinAPIError(error_code err) : std::runtime_error(format_error(err)) {}
170 
171 private:
172  static std::string format_error(error_code err)
173  {
174  LPWSTR buffer = nullptr;
175  FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
176  nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&buffer), 0, nullptr);
177  StdStrBuf str(buffer);
178  LocalFree(buffer);
179  return std::string(str.getData(), str.getLength());
180  }
181 };
182 
183 class GLTempContext
184 {
185  HWND wnd;
186  HDC dc;
187  HGLRC glrc;
188 public:
189  GLTempContext()
190  {
191  wnd = CreateWindowExW(0, L"STATIC", nullptr, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
192  if (!wnd)
193  throw WinAPIError();
194  dc = GetDC(wnd);
195  auto pfd = PIXELFORMATDESCRIPTOR();
196  pfd.nSize = sizeof(pfd);
197  pfd.nVersion = 1;
198  pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
199  pfd.iPixelType = PFD_TYPE_RGBA;
200  pfd.iLayerType = PFD_MAIN_PLANE;
201  int format = ChoosePixelFormat(dc, &pfd);
202  if (!format ||
203  !SetPixelFormat(dc, format, &pfd) ||
204  (glrc = wglCreateContext(dc)) == nullptr)
205  {
206  DWORD err = GetLastError();
207  ReleaseDC(wnd, dc);
208  DestroyWindow(wnd);
209  throw WinAPIError(err);
210  }
211  if (!wglMakeCurrent(dc, glrc))
212  {
213  DWORD err = GetLastError();
214  wglDeleteContext(glrc);
215  ReleaseDC(wnd, dc);
216  DestroyWindow(wnd);
217  throw WinAPIError(err);
218  }
219  }
220  ~GLTempContext()
221  {
222  if (glrc == wglGetCurrentContext())
223  wglMakeCurrent(dc, nullptr);
224  wglDeleteContext(glrc);
225  ReleaseDC(wnd, dc);
226  DestroyWindow(wnd);
227  }
228 };
229 
230 CStdGLCtx::CStdGLCtx(): this_context(contexts.end()) { }
231 
232 void CStdGLCtx::Clear(bool multisample_change)
233 {
234  Deselect();
235  if (hDC && pWindow)
236  ReleaseDC(pWindow->renderwnd, hDC);
237  hDC = nullptr;
238  pWindow = nullptr;
239 
240  if (this_context != contexts.end())
241  {
242  contexts.erase(this_context);
243  this_context = contexts.end();
244  }
245  if (multisample_change)
246  {
247  assert(!pGL->pCurrCtx);
248  if (hrc)
249  wglDeleteContext(hrc);
250  hrc = nullptr;
251  }
252 }
253 
254 bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *pApp)
255 {
256  // safety
257  if (!pGL || !pWindow) return false;
258 
259  std::unique_ptr<GLTempContext> tempContext;
260  if (hrc == nullptr)
261  {
262  // Create a temporary context to be able to fetch GL extension pointers
263  try
264  {
265  tempContext = std::make_unique<GLTempContext>();
266  glewExperimental = GL_TRUE;
267  GLenum err = glewInit();
268  if(err != GLEW_OK)
269  {
270  // Problem: glewInit failed, something is seriously wrong.
271  pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
272  return false;
273  }
274  }
275  catch (const WinAPIError &e)
276  {
277  pGL->Error((std::string(" gl: Unable to create temporary context: ") + e.what()).c_str());
278  return false;
279  }
280  }
281 
282  // store window
283  this->pWindow = pWindow;
284 
285  // get DC
286  hDC = GetDC(pWindow->renderwnd);
287  if(!hDC)
288  {
289  pGL->Error(" gl: Error getting DC");
290  return false;
291  }
292  if (hrc)
293  {
294  SetPixelFormat(hDC, pGL->iPixelFormat, &pfd);
295  }
296  else
297  {
298  // Choose a good pixel format.
299  int pixel_format;
300  if((pixel_format = GetPixelFormatForMS(hDC, Config.Graphics.MultiSampling)) == 0)
301  if((pixel_format = GetPixelFormatForMS(hDC, 0)) != 0)
303 
304  if (!pixel_format)
305  {
306  pGL->Error(" gl: Error choosing pixel format");
307  }
308  else
309  {
310  ZeroMemory(&pfd, sizeof(pfd)); pfd.nSize = sizeof(pfd);
311  if(!DescribePixelFormat(hDC, pixel_format, sizeof(pfd), &pfd))
312  {
313  pGL->Error(" gl: Error describing chosen pixel format");
314  }
315  else if(!SetPixelFormat(hDC, pixel_format, &pfd))
316  {
317  pGL->Error(" gl: Error setting chosen pixel format");
318  }
319  else
320  {
321  // create context
322  if (wglCreateContextAttribsARB)
323  {
324  {
325  const int attribs[] = {
326  WGL_CONTEXT_FLAGS_ARB, Config.Graphics.DebugOpenGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
327  WGL_CONTEXT_MAJOR_VERSION_ARB, REQUESTED_GL_CTX_MAJOR,
328  WGL_CONTEXT_MINOR_VERSION_ARB, REQUESTED_GL_CTX_MINOR,
329  WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
330  0
331  };
332 
333  hrc = wglCreateContextAttribsARB(hDC, nullptr, attribs);
334  }
335 
336  if (!hrc)
337  {
338  LogSilentF(" gl: OpenGL %d.%d not available; falling back to 3.1 emergency context.", REQUESTED_GL_CTX_MAJOR, REQUESTED_GL_CTX_MINOR);
339  // Some older Intel drivers don't support OpenGL 3.2; we don't use (much?) of
340  // that so we'll request a 3.1 context as a fallback.
341  const int attribs[] = {
342  WGL_CONTEXT_FLAGS_ARB, Config.Graphics.DebugOpenGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
343  WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
344  WGL_CONTEXT_MINOR_VERSION_ARB, 1,
345  0
346  };
347  pGL->Workarounds.ForceSoftwareTransform = true;
348  hrc = wglCreateContextAttribsARB(hDC, nullptr, attribs);
349  }
350  }
351  else
352  {
353  DebugLog(" gl: wglCreateContextAttribsARB not available; creating default context.");
354  hrc = wglCreateContext(hDC);
355  }
356 
357  if(!hrc)
358  {
359  pGL->Error(" gl: Error creating gl context");
360  }
361 
362  pGL->iPixelFormat = pixel_format;
363  }
364  }
365  }
366  if (hrc)
367  {
368  Select();
369  // After selecting the new context, we have to reinitialize GLEW to
370  // update its function pointers - the driver may elect to expose
371  // different extensions depending on the context attributes
372  glewExperimental = GL_TRUE;
373  GLenum err = glewInit();
374  if (err != GLEW_OK)
375  {
376  // Uh. This is a problem.
377  pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
378  return false;
379  }
380 
381  this_context = contexts.insert(contexts.end(), this);
382  return true;
383  }
384 
385  ReleaseDC(pWindow->renderwnd, hDC); hDC = nullptr;
386  return false;
387 }
388 
389 std::vector<int> CStdGLCtx::EnumerateMultiSamples() const
390 {
391  std::vector<int> result;
392  std::vector<int> vec = EnumeratePixelFormats(hDC);
393  for(int i : vec)
394  {
395  int attributes[] = { WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB };
396  const unsigned int n_attributes = 2;
397  int results[2];
398  if(!wglGetPixelFormatAttribivARB(hDC, i, 0, n_attributes, attributes, results)) continue;
399 
400  if(results[0] == 1) result.push_back(results[1]);
401  }
402 
403  return result;
404 }
405 
406 bool CStdGLCtx::Select(bool verbose)
407 {
408  // safety
409  if (!pGL || !hrc) return false;
410  // make context current
411  if (!wglMakeCurrent (hDC, hrc))
412  {
413  pGL->Error("Unable to select context.");
414  return false;
415  }
416  SelectCommon();
417  // update clipper - might have been done by UpdateSize
418  // however, the wrong size might have been assumed
419  if (!pGL->UpdateClipper()) return false;
420  // success
421  return true;
422 }
423 
424 void CStdGLCtx::Deselect()
425 {
426  if (pGL && pGL->pCurrCtx == this)
427  {
428  wglMakeCurrent(nullptr, nullptr);
429  pGL->pCurrCtx=nullptr;
430  pGL->RenderTarget=nullptr;
431  }
432 }
433 
434 bool CStdGLCtx::PageFlip()
435 {
436  // flush GL buffer
437  glFlush();
438  SwapBuffers(hDC);
439  return true;
440 }
441 
442 #elif defined(USE_SDL_MAINLOOP)
443 
444 CStdGLCtx::CStdGLCtx(): pWindow(0), this_context(contexts.end()) { ctx = nullptr; }
445 
446 void CStdGLCtx::Clear(bool multisample_change)
447 {
448  Deselect();
449  if (ctx) SDL_GL_DeleteContext(ctx);
450  ctx = 0;
451  pWindow = 0;
452 
453  if (this_context != contexts.end())
454  {
455  contexts.erase(this_context);
456  this_context = contexts.end();
457  }
458 }
459 
460 bool CStdGLCtx::Init(C4Window * pWindow, C4AbstractApp *)
461 {
462  // safety
463  if (!pGL) return false;
464  // store window
465  this->pWindow = pWindow;
466  ctx = SDL_GL_CreateContext(pWindow->window);
467  if (!ctx)
468  {
469  LogSilentF(" gl: OpenGL %d.%d not available; falling back to 3.1 emergency context.", REQUESTED_GL_CTX_MAJOR, REQUESTED_GL_CTX_MINOR);
470  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
471  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
472  pGL->Workarounds.ForceSoftwareTransform = true;
473  ctx = SDL_GL_CreateContext(pWindow->window);
474  }
475  if (!ctx)
476  {
477  return pGL->Error(FormatString("SDL_GL_CreateContext: %s", SDL_GetError()).getData());
478  }
479  // No luck at all?
480  if (!Select(true)) return false;
481  // init extensions
482  glewExperimental = GL_TRUE;
483  GLenum err = glewInit();
484  if (GLEW_OK != err)
485  {
486  // Problem: glewInit failed, something is seriously wrong.
487  return pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
488  }
489 
490  this_context = contexts.insert(contexts.end(), this);
491  return true;
492 }
493 
494 bool CStdGLCtx::Select(bool verbose)
495 {
496  if (SDL_GL_MakeCurrent(pWindow->window, ctx) != 0)
497  return pGL->Error(FormatString("SDL_GL_MakeCurrent: %s", SDL_GetError()).getData());
498  SelectCommon();
499  // update clipper - might have been done by UpdateSize
500  // however, the wrong size might have been assumed
501  if (!pGL->UpdateClipper())
502  {
503  if (verbose) pGL->Error(" gl: UpdateClipper failed");
504  return false;
505  }
506  // success
507  return true;
508 }
509 
510 void CStdGLCtx::Deselect()
511 {
512  if (pGL && pGL->pCurrCtx == this)
513  {
514  pGL->pCurrCtx = 0;
515  pGL->RenderTarget = 0;
516  }
517 }
518 
519 bool CStdGLCtx::PageFlip()
520 {
521  // flush GL buffer
522  glFlush();
523  if (!pWindow) return false;
524  SDL_GL_SwapWindow(pWindow->window);
525  return true;
526 }
527 
528 #endif // USE_*
529 
530 #ifdef WITH_QT_EDITOR
531 #undef LineFeed // conflicts with Qt
532 #undef new
533 #undef delete
534 #include <QOpenGLWidget>
535 #include <QOpenGLContext>
536 #include <QOffscreenSurface>
537 
538 CStdGLCtxQt::CStdGLCtxQt() { context = nullptr; surface = nullptr; }
539 
540 void CStdGLCtxQt::Clear(bool multisample_change)
541 {
542  Deselect();
543 
544  if (context)
545  {
546  if (!pWindow->glwidget) delete context;
547  delete surface;
548  }
549  pWindow = nullptr;
550 }
551 
552 bool CStdGLCtxQt::Init(C4Window *window, C4AbstractApp *app)
553 {
554  if (!pGL) return false;
555  pWindow = window;
556 
557  if (!pWindow->glwidget)
558  {
559  surface = new QOffscreenSurface();
560  surface->create();
561  context = new QOpenGLContext();
562  QOpenGLContext* share_context = QOpenGLContext::globalShareContext();
563  if (share_context) context->setShareContext(share_context);
564  if (!context->create())
565  return false;
566 
567  if (!Select(true)) return false;
568 
569  // init extensions
570  glewExperimental = GL_TRUE;
571  GLenum err = glewInit();
572  if (GLEW_OK != err)
573  {
574  // Problem: glewInit failed, something is seriously wrong.
575  return pGL->Error(reinterpret_cast<const char*>(glewGetErrorString(err)));
576  }
577  }
578  else
579  {
580  // The Qt GL widget has its own context
581  context = pWindow->glwidget->context();
582  }
583 
584  this_context = contexts.insert(contexts.end(), this);
585  return true;
586 }
587 
588 bool CStdGLCtxQt::Select(bool verbose)
589 {
590  if (!pWindow->glwidget)
591  {
592  if (!context->makeCurrent(surface))
593  return false;
594  }
595  else
596  {
597  // done automatically
598  /* pWindow->glwidget->makeCurrent(); */
599  }
600  SelectCommon();
601  // update clipper - might have been done by UpdateSize
602  // however, the wrong size might have been assumed
603  if (!pGL->UpdateClipper())
604  {
605  if (verbose) pGL->Error(" gl: UpdateClipper failed");
606  return false;
607  }
608  // success
609  return true;
610 }
611 
612 void CStdGLCtxQt::Deselect()
613 {
614  if (!pWindow->glwidget)
615  context->doneCurrent();
616  else
617  {
618  // done automatically
619  /* pWindow->glwidget->doneCurrent(); */
620  }
621  if (pGL && pGL->pCurrCtx == this)
622  {
623  pGL->pCurrCtx = nullptr;
624  pGL->RenderTarget = nullptr;
625  }
626 }
627 
628 bool CStdGLCtxQt::PageFlip()
629 {
630  // flush GL buffer
631  glFlush();
632  if (!pWindow) return false;
633  if (!pWindow->glwidget)
634  return false;
635  return true;
636 }
637 
638 #endif
639 
640 #endif // USE_CONSOLE
void SelectCommon()
Definition: C4DrawGLCtx.cpp:33
int32_t DebugOpenGL
Definition: C4Config.h:117
int32_t MultiSampling
Definition: C4Config.h:115
C4Config Config
Definition: C4Config.cpp:833
virtual bool Select(bool verbose=false)
static std::list< CStdGLCtx * > contexts
Definition: C4DrawGL.h:135
CStdGLCtx * pCurrCtx
Definition: C4DrawGL.h:179
virtual void Deselect()
bool DebugLog(const char *strMessage)
Definition: C4Log.cpp:270
std::vector< unsigned int > VAOsToBeDeleted
Definition: C4DrawGL.h:141
C4ConfigGraphics Graphics
Definition: C4Config.h:253
bool UpdateClipper() override
Definition: C4DrawGL.cpp:151
bool LogSilentF(const char *strMessage,...)
Definition: C4Log.cpp:260
int iPixelFormat
Definition: C4DrawGL.h:175
std::list< CStdGLCtx * >::iterator this_context
Definition: C4DrawGL.h:136
virtual bool PageFlip()
C4Window * pWindow
Definition: C4DrawGL.h:127
struct CStdGL::@10 Workarounds
virtual bool Init(C4Window *pWindow, C4AbstractApp *pApp)
C4Surface * RenderTarget
Definition: C4Draw.h:107
bool Error(const char *szMsg) override
Definition: C4DrawGL.cpp:853
virtual void Clear(bool multisample_change=false)
CStdGL * pGL
Definition: C4DrawGL.cpp:905
uint32_t DWORD
std::vector< GLuint > hVAOs
Definition: C4DrawGL.h:139
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270