1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <memory>
11 #include <string_view>
12 #include <thread>
13 #include <vcl/opengl/OpenGLContext.hxx>
14 #include <vcl/opengl/OpenGLHelper.hxx>
15 
16 #include <sal/log.hxx>
17 #include <comphelper/windowserrorstring.hxx>
18 #include <opengl/zone.hxx>
19 #include <win/wincomp.hxx>
20 #include <win/saldata.hxx>
21 #include <win/salframe.h>
22 #include <win/salinst.h>
23 #include <epoxy/wgl.h>
24 #include <ControlCacheKey.hxx>
25 
26 static std::vector<HGLRC> g_vShareList;
27 static bool g_bAnyCurrent;
28 
29 namespace {
30 
31 class GLWinWindow : public GLWindow
32 {
33 public:
34     HWND                    hWnd;
35     HDC                     hDC;
36     HGLRC                   hRC;
37     GLWinWindow();
38 };
39 
40 }
41 
GLWinWindow()42 GLWinWindow::GLWinWindow()
43     : hWnd(nullptr)
44     , hDC(nullptr)
45     , hRC(nullptr)
46 {
47 }
48 
49 namespace {
50 
51 class WinOpenGLContext : public OpenGLContext
52 {
53 public:
54     virtual void initWindow() override;
55 private:
56     GLWinWindow m_aGLWin;
getOpenGLWindow() const57     virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
getModifiableOpenGLWindow()58     virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
59     virtual bool ImplInit() override;
60     virtual void makeCurrent() override;
61     virtual void destroyCurrentContext() override;
62     virtual bool isCurrent() override;
63     virtual bool isAnyCurrent() override;
64     virtual void resetCurrent() override;
65     virtual void swapBuffers() override;
66 };
67 
68 }
69 
swapBuffers()70 void WinOpenGLContext::swapBuffers()
71 {
72     OpenGLZone aZone;
73 
74     SwapBuffers(m_aGLWin.hDC);
75 
76     BuffersSwapped();
77 }
78 
resetCurrent()79 void WinOpenGLContext::resetCurrent()
80 {
81     clearCurrent();
82 
83     OpenGLZone aZone;
84 
85     wglMakeCurrent(nullptr, nullptr);
86     g_bAnyCurrent = false;
87 }
88 
ensureDispatchTable()89 static void ensureDispatchTable()
90 {
91     thread_local bool bEpoxyDispatchMakeCurrentCalled = false;
92     if (!bEpoxyDispatchMakeCurrentCalled)
93     {
94         epoxy_handle_external_wglMakeCurrent();
95         bEpoxyDispatchMakeCurrentCalled = true;
96     }
97 }
98 
isCurrent()99 bool WinOpenGLContext::isCurrent()
100 {
101     OpenGLZone aZone;
102     if (!g_bAnyCurrent || !m_aGLWin.hRC)
103         return false;
104     ensureDispatchTable();
105     return wglGetCurrentContext() == m_aGLWin.hRC && wglGetCurrentDC() == m_aGLWin.hDC;
106 }
107 
isAnyCurrent()108 bool WinOpenGLContext::isAnyCurrent()
109 {
110     return g_bAnyCurrent && wglGetCurrentContext() != nullptr;
111 }
112 
makeCurrent()113 void WinOpenGLContext::makeCurrent()
114 {
115     if (isCurrent())
116         return;
117 
118     OpenGLZone aZone;
119 
120     clearCurrent();
121 
122     ensureDispatchTable();
123 
124     if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
125     {
126         g_bAnyCurrent = false;
127         DWORD nLastError = GetLastError();
128         if (nLastError != ERROR_SUCCESS)
129             SAL_WARN("vcl.opengl", "wglMakeCurrent failed: " << WindowsErrorString(nLastError));
130         return;
131     }
132 
133     g_bAnyCurrent = true;
134 
135     registerAsCurrent();
136 }
137 
initWindow()138 void WinOpenGLContext::initWindow()
139 {
140     if( !m_pChildWindow )
141     {
142         SystemWindowData winData = generateWinData(mpWindow, false);
143         m_pChildWindow = VclPtr<SystemChildWindow>::Create(mpWindow, 0, &winData, false);
144     }
145 
146     if (m_pChildWindow)
147     {
148         InitChildWindow(m_pChildWindow.get());
149         const SystemEnvData* sysData(m_pChildWindow->GetSystemData());
150         m_aGLWin.hWnd = sysData->hWnd;
151     }
152 
153     m_aGLWin.hDC = GetDC(m_aGLWin.hWnd);
154 }
155 
destroyCurrentContext()156 void WinOpenGLContext::destroyCurrentContext()
157 {
158     if (m_aGLWin.hRC)
159     {
160         std::vector<HGLRC>::iterator itr = std::remove(g_vShareList.begin(), g_vShareList.end(), m_aGLWin.hRC);
161         if (itr != g_vShareList.end())
162             g_vShareList.erase(itr);
163 
164         if (wglGetCurrentContext() != nullptr)
165         {
166             wglMakeCurrent(nullptr, nullptr);
167             g_bAnyCurrent = false;
168         }
169         wglDeleteContext( m_aGLWin.hRC );
170         ReleaseDC( m_aGLWin.hWnd, m_aGLWin.hDC );
171         m_aGLWin.hRC = nullptr;
172     }
173 }
174 
WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)175 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
176 {
177     switch (message)
178     {
179     case WM_CREATE:
180         return 0;
181     case WM_CLOSE:
182         PostQuitMessage(0);
183         return 0;
184     case WM_DESTROY:
185         return 0;
186     default:
187         return DefWindowProcW(hwnd, message, wParam, lParam);
188     }
189 }
190 
InitTempWindow(HWND & hwnd,int width,int height,const PIXELFORMATDESCRIPTOR & inPfd,GLWinWindow & glWin)191 static bool InitTempWindow(HWND& hwnd, int width, int height, const PIXELFORMATDESCRIPTOR& inPfd, GLWinWindow& glWin)
192 {
193     OpenGLZone aZone;
194 
195     PIXELFORMATDESCRIPTOR  pfd = inPfd;
196     int ret;
197     WNDCLASSW wc;
198     wc.style = 0;
199     wc.lpfnWndProc = WndProc;
200     wc.cbClsExtra = wc.cbWndExtra = 0;
201     wc.hInstance = nullptr;
202     wc.hIcon = nullptr;
203     wc.hCursor = nullptr;
204     wc.hbrBackground = nullptr;
205     wc.lpszMenuName = nullptr;
206     wc.lpszClassName = L"GLRenderer";
207     RegisterClassW(&wc);
208     hwnd = CreateWindowW(wc.lpszClassName, nullptr, WS_DISABLED, 0, 0, width, height, nullptr, nullptr, wc.hInstance, nullptr);
209     glWin.hDC = GetDC(hwnd);
210 
211     int nPixelFormat = ChoosePixelFormat(glWin.hDC, &pfd);
212     if (!nPixelFormat)
213     {
214         ReleaseDC(hwnd, glWin.hDC);
215         DestroyWindow(hwnd);
216         return false;
217     }
218     ret = SetPixelFormat(glWin.hDC, nPixelFormat, &pfd);
219     if(!ret)
220     {
221         ReleaseDC(hwnd, glWin.hDC);
222         DestroyWindow(hwnd);
223         return false;
224     }
225     glWin.hRC = wglCreateContext(glWin.hDC);
226     if(!(glWin.hRC))
227     {
228         ReleaseDC(hwnd, glWin.hDC);
229         DestroyWindow(hwnd);
230         return false;
231     }
232     ret = wglMakeCurrent(glWin.hDC, glWin.hRC);
233     if(!ret)
234     {
235         wglMakeCurrent(nullptr, nullptr);
236         g_bAnyCurrent = false;
237         wglDeleteContext(glWin.hRC);
238         ReleaseDC(hwnd, glWin.hDC);
239         DestroyWindow(hwnd);
240         return false;
241     }
242     g_bAnyCurrent = false;
243 
244     return true;
245 }
246 
WGLisExtensionSupported(const char * extension)247 static bool WGLisExtensionSupported(const char *extension)
248 {
249     OpenGLZone aZone;
250 
251     const size_t extlen = strlen(extension);
252     const char *supported = nullptr;
253 
254     // Try to use wglGetExtensionStringARB on current DC, if possible
255     PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");
256 
257     if (wglGetExtString)
258         supported = reinterpret_cast<char*(__stdcall*)(HDC)>(wglGetExtString)(wglGetCurrentDC());
259     // If that failed, try standard OpenGL extensions string
260     if (supported == nullptr)
261         supported = reinterpret_cast<char const *>(glGetString(GL_EXTENSIONS));
262     // If that failed too, must be no extensions supported
263     if (supported == nullptr)
264         return false;
265 
266     // Begin examination at start of string, increment by 1 on false match
267     for (const char* p = supported; ; p++)
268     {
269         // Advance p up to the next possible match
270         p = strstr(p, extension);
271 
272         if (p == nullptr)
273             return false; // No Match
274 
275         // Make sure that match is at the start of the string or that
276         // the previous char is a space, or else we could accidentally
277         // match "wglFunkywglExtension" with "wglExtension"
278 
279         // Also, make sure that the following character is space or null
280         // or else "wglExtensionTwo" might match "wglExtension"
281         if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
282             return true; // Match
283     }
284 }
285 
InitMultisample(const PIXELFORMATDESCRIPTOR & pfd,int & rPixelFormat,bool bUseDoubleBufferedRendering,bool bRequestVirtualDevice)286 static bool InitMultisample(const PIXELFORMATDESCRIPTOR& pfd, int& rPixelFormat,
287         bool bUseDoubleBufferedRendering, bool bRequestVirtualDevice)
288 {
289     OpenGLZone aZone;
290 
291     HWND hWnd = nullptr;
292     GLWinWindow glWin;
293     // Create a temp window to check whether support multi-sample, if support, get the format
294     if (!InitTempWindow(hWnd, 32, 32, pfd, glWin))
295     {
296         SAL_WARN("vcl.opengl", "Can't create temp window to test");
297         return false;
298     }
299 
300     // See if the string exists in WGL
301     if (!WGLisExtensionSupported("WGL_ARB_multisample"))
302     {
303         SAL_WARN("vcl.opengl", "Device doesn't support multisample");
304         wglMakeCurrent(nullptr, nullptr);
305         g_bAnyCurrent = false;
306         wglDeleteContext(glWin.hRC);
307         ReleaseDC(hWnd, glWin.hDC);
308         DestroyWindow(hWnd);
309         return false;
310     }
311     // Get our pixel format
312     PFNWGLCHOOSEPIXELFORMATARBPROC fn_wglChoosePixelFormatARB = reinterpret_cast<PFNWGLCHOOSEPIXELFORMATARBPROC>(wglGetProcAddress("wglChoosePixelFormatARB"));
313     if (!fn_wglChoosePixelFormatARB)
314     {
315         wglMakeCurrent(nullptr, nullptr);
316         g_bAnyCurrent = false;
317         wglDeleteContext(glWin.hRC);
318         ReleaseDC(hWnd, glWin.hDC);
319         DestroyWindow(hWnd);
320         return false;
321     }
322     // Get our current device context
323     HDC hDC = GetDC(hWnd);
324 
325     int pixelFormat;
326     int valid;
327     UINT    numFormats;
328     float   fAttributes[] = {0,0};
329     // These attributes are the bits we want to test for in our sample.
330     // Everything is pretty standard, the only one we want to
331     // really focus on is the WGL_SAMPLE_BUFFERS_ARB and WGL_SAMPLES_ARB.
332     // These two are going to do the main testing for whether or not
333     // we support multisampling on this hardware.
334     int iAttributes[] =
335     {
336         WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
337         WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
338         WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
339         WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
340         WGL_COLOR_BITS_ARB,24,
341         WGL_ALPHA_BITS_ARB,8,
342         WGL_DEPTH_BITS_ARB,24,
343         WGL_STENCIL_BITS_ARB,0,
344         WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
345         WGL_SAMPLES_ARB,8,
346         0,0
347     };
348 
349     if (!bUseDoubleBufferedRendering)
350     {
351         // Use asserts to make sure the iAttributes array is not changed without changing these ugly
352         // hardcode indexes into it.
353         assert(iAttributes[0] == WGL_DOUBLE_BUFFER_ARB);
354         iAttributes[1] = GL_FALSE;
355     }
356 
357     if (bRequestVirtualDevice)
358     {
359         assert(iAttributes[2] == WGL_DRAW_TO_WINDOW_ARB);
360         iAttributes[2] = WGL_DRAW_TO_BITMAP_ARB;
361     }
362 
363     bool bArbMultisampleSupported = false;
364 
365     // First we check to see if we can get a pixel format for 8 samples
366     valid = fn_wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
367     // If we returned true, and our format count is greater than 1
368     if (valid && numFormats >= 1)
369     {
370         bArbMultisampleSupported = true;
371         rPixelFormat = pixelFormat;
372         wglMakeCurrent(nullptr, nullptr);
373         g_bAnyCurrent = false;
374         wglDeleteContext(glWin.hRC);
375         ReleaseDC(hWnd, glWin.hDC);
376         DestroyWindow(hWnd);
377         return bArbMultisampleSupported;
378     }
379     // Our pixel format with 8 samples failed, test for 2 samples
380     assert(iAttributes[18] == WGL_SAMPLES_ARB);
381     iAttributes[19] = 2;
382     valid = fn_wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
383     if (valid && numFormats >= 1)
384     {
385         bArbMultisampleSupported = true;
386         rPixelFormat = pixelFormat;
387         wglMakeCurrent(nullptr, nullptr);
388         g_bAnyCurrent = false;
389         wglDeleteContext(glWin.hRC);
390         ReleaseDC(hWnd, glWin.hDC);
391         DestroyWindow(hWnd);
392         return bArbMultisampleSupported;
393     }
394     // Return the valid format
395     wglMakeCurrent(nullptr, nullptr);
396     g_bAnyCurrent = false;
397     wglDeleteContext(glWin.hRC);
398     ReleaseDC(hWnd, glWin.hDC);
399     DestroyWindow(hWnd);
400 
401     return bArbMultisampleSupported;
402 }
403 
404 namespace
405 {
406 
tryShaders(const OUString & rVertexShader,const OUString & rFragmentShader,const OUString & rGeometryShader="",std::string_view rPreamble="")407 bool tryShaders(const OUString& rVertexShader, const OUString& rFragmentShader, const OUString& rGeometryShader = "", std::string_view rPreamble = "")
408 {
409     GLint nId;
410 
411     // Somewhat mysteriously, the OpenGLHelper::LoadShaders() API saves a compiled binary of the
412     // shader only if you give it the digest of the shaders. We have API to calculate the digest
413     // only of the combination of vertex and fragment (but not geometry) shader. So if we have a
414     // geometry shader, we should not save the binary.
415     if (rGeometryShader.isEmpty())
416     {
417         nId = OpenGLHelper::LoadShaders(rVertexShader, rFragmentShader, rPreamble, OpenGLHelper::GetDigest( rVertexShader, rFragmentShader, rPreamble));
418     }
419     else
420     {
421         assert(rPreamble.empty());
422         nId = OpenGLHelper::LoadShaders(rVertexShader, rFragmentShader, rGeometryShader);
423     }
424     if (!nId)
425         return false;
426 
427     // We're interested in the error returned by glDeleteProgram().
428     glGetError();
429 
430     glDeleteProgram(nId);
431     return glGetError() == GL_NO_ERROR;
432 }
433 
compiledShaderBinariesWork()434 bool compiledShaderBinariesWork()
435 {
436     static bool bBeenHere = false;
437     static bool bResult;
438 
439     if (bBeenHere)
440         return bResult;
441 
442     bBeenHere = true;
443 
444     bResult =
445         (
446 #if 0 // Only look at shaders used by slideshow for now
447          // canvas
448          tryShaders("dummyVertexShader", "linearMultiColorGradientFragmentShader") &&
449          tryShaders("dummyVertexShader", "linearTwoColorGradientFragmentShader") &&
450          tryShaders("dummyVertexShader", "radialMultiColorGradientFragmentShader") &&
451          tryShaders("dummyVertexShader", "radialTwoColorGradientFragmentShader") &&
452          tryShaders("dummyVertexShader", "rectangularMultiColorGradientFragmentShader") &&
453          tryShaders("dummyVertexShader", "rectangularTwoColorGradientFragmentShader") &&
454 #endif
455          // slideshow
456          tryShaders("reflectionVertexShader", "reflectionFragmentShader") &&
457          tryShaders("basicVertexShader", "basicFragmentShader") &&
458          tryShaders("vortexVertexShader", "vortexFragmentShader", "vortexGeometryShader") &&
459          tryShaders("basicVertexShader", "rippleFragmentShader") &&
460          tryShaders("glitterVertexShader", "glitterFragmentShader") &&
461          tryShaders("honeycombVertexShader", "honeycombFragmentShader", "honeycombGeometryShader"));
462 
463     return bResult;
464 }
465 
466 } // unnamed namespace
467 
ImplInit()468 bool WinOpenGLContext::ImplInit()
469 {
470     static bool bFirstCall = true;
471 
472     OpenGLZone aZone;
473 
474     VCL_GL_INFO("OpenGLContext::ImplInit----start");
475     // PixelFormat tells Windows how we want things to be
476     PIXELFORMATDESCRIPTOR PixelFormatFront =
477     {
478         sizeof(PIXELFORMATDESCRIPTOR),
479         1,                              // Version Number
480         PFD_SUPPORT_OPENGL,
481         PFD_TYPE_RGBA,                  // Request An RGBA Format
482         BYTE(32),                       // Select Our Color Depth
483         0, 0, 0, 0, 0, 0,               // Color Bits Ignored
484         0,                              // No Alpha Buffer
485         0,                              // Shift Bit Ignored
486         0,                              // No Accumulation Buffer
487         0, 0, 0, 0,                     // Accumulation Bits Ignored
488         24,                             // 24 bit z-buffer
489         8,                              // stencil buffer
490         0,                              // No Auxiliary Buffer
491         0,                              // now ignored
492         0,                              // Reserved
493         0, 0, 0                         // Layer Masks Ignored
494     };
495 
496     PixelFormatFront.dwFlags |= PFD_DOUBLEBUFFER;
497     PixelFormatFront.dwFlags |= PFD_DRAW_TO_WINDOW;
498 
499     //  we must check whether can set the MSAA
500     int WindowPix = 0;
501     bool bMultiSampleSupport = false;
502 
503     bMultiSampleSupport = InitMultisample(PixelFormatFront, WindowPix, /*bUseDoubleBufferedRendering*/true, false);
504 
505     if (bMultiSampleSupport && WindowPix != 0)
506     {
507         m_aGLWin.bMultiSampleSupported = true;
508     }
509     else
510     {
511         WindowPix = ChoosePixelFormat(m_aGLWin.hDC, &PixelFormatFront);
512 #if OSL_DEBUG_LEVEL > 0
513         PIXELFORMATDESCRIPTOR pfd;
514         DescribePixelFormat(m_aGLWin.hDC, WindowPix, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
515         SAL_WARN("vcl.opengl", "Render Target: Window: " << static_cast<int>((pfd.dwFlags & PFD_DRAW_TO_WINDOW) != 0) << ", Bitmap: " << static_cast<int>((pfd.dwFlags & PFD_DRAW_TO_BITMAP) != 0));
516         SAL_WARN("vcl.opengl", "Supports OpenGL: " << static_cast<int>((pfd.dwFlags & PFD_SUPPORT_OPENGL) != 0));
517 #endif
518     }
519 
520     if (WindowPix == 0)
521     {
522         SAL_WARN("vcl.opengl", "Invalid pixelformat");
523         return false;
524     }
525 
526     if (!SetPixelFormat(m_aGLWin.hDC, WindowPix, &PixelFormatFront))
527     {
528         SAL_WARN("vcl.opengl", "SetPixelFormat failed: " << WindowsErrorString(GetLastError()));
529         return false;
530     }
531 
532     HGLRC hTempRC = wglCreateContext(m_aGLWin.hDC);
533     if (hTempRC == nullptr)
534     {
535         SAL_WARN("vcl.opengl", "wglCreateContext failed: "<< WindowsErrorString(GetLastError()));
536         return false;
537     }
538 
539     if (!wglMakeCurrent(m_aGLWin.hDC, hTempRC))
540     {
541         g_bAnyCurrent = false;
542         SAL_WARN("vcl.opengl", "wglMakeCurrent failed: "<< WindowsErrorString(GetLastError()));
543         return false;
544     }
545 
546     g_bAnyCurrent = true;
547 
548     if (!InitGL())
549     {
550         wglMakeCurrent(nullptr, nullptr);
551         g_bAnyCurrent = false;
552         wglDeleteContext(hTempRC);
553         return false;
554     }
555 
556     HGLRC hSharedCtx = nullptr;
557     if (!g_vShareList.empty())
558         hSharedCtx = g_vShareList.front();
559 
560     if (!wglCreateContextAttribsARB)
561     {
562         wglMakeCurrent(nullptr, nullptr);
563         g_bAnyCurrent = false;
564         wglDeleteContext(hTempRC);
565         return false;
566     }
567 
568     // now setup the shared context; this needs a temporary context already
569     // set up in order to work
570     int const attribs [] =
571     {
572 #ifdef DBG_UTIL
573         WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
574 #endif
575         0
576     };
577     m_aGLWin.hRC = wglCreateContextAttribsARB(m_aGLWin.hDC, hSharedCtx, attribs);
578     if (m_aGLWin.hRC == nullptr)
579     {
580         SAL_WARN("vcl.opengl", "wglCreateContextAttribsARB failed: "<< WindowsErrorString(GetLastError()));
581         wglMakeCurrent(nullptr, nullptr);
582         g_bAnyCurrent = false;
583         wglDeleteContext(hTempRC);
584         return false;
585     }
586 
587     if (!compiledShaderBinariesWork())
588     {
589         wglMakeCurrent(nullptr, nullptr);
590         g_bAnyCurrent = false;
591         wglDeleteContext(hTempRC);
592         return false;
593     }
594 
595     wglMakeCurrent(nullptr, nullptr);
596     g_bAnyCurrent = false;
597     wglDeleteContext(hTempRC);
598 
599     if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
600     {
601         g_bAnyCurrent = false;
602         SAL_WARN("vcl.opengl", "wglMakeCurrent failed: " << WindowsErrorString(GetLastError()));
603         return false;
604     }
605 
606     g_bAnyCurrent = true;
607 
608     if (bFirstCall)
609     {
610         // Checking texture size
611         GLint nMaxTextureSize;
612         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);
613         if (nMaxTextureSize <= 4096)
614             SAL_WARN("vcl.opengl", "Max texture size is " << nMaxTextureSize
615                                     << ". This may not be enough for normal operation.");
616         else
617             VCL_GL_INFO("Max texture size: " << nMaxTextureSize);
618 
619         // Trying to make a texture and check its size
620         for (GLint nWidthHeight = 1023; nWidthHeight < nMaxTextureSize; nWidthHeight += (nWidthHeight + 1))
621         {
622             glTexImage2D(GL_PROXY_TEXTURE_2D, 0, 4, nWidthHeight, nWidthHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, nullptr);
623             CHECK_GL_ERROR();
624             if (glGetError() == GL_NO_ERROR)
625             {
626                 GLint nWidth = 0;
627                 GLint nHeight = 0;
628                 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &nWidth);
629                 glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &nHeight);
630                 VCL_GL_INFO("Created texture " << nWidthHeight << "," << nWidthHeight << " reports size: " << nWidth << ", " << nHeight);
631             }
632             else
633             {
634                 SAL_WARN("vcl.opengl", "Error when creating a " << nWidthHeight << ", " << nWidthHeight << " test texture.");
635             }
636         }
637     }
638 
639     InitGLDebugging();
640 
641     g_vShareList.push_back(m_aGLWin.hRC);
642 
643     RECT clientRect;
644     GetClientRect(WindowFromDC(m_aGLWin.hDC), &clientRect);
645     m_aGLWin.Width = clientRect.right - clientRect.left;
646     m_aGLWin.Height = clientRect.bottom - clientRect.top;
647 
648     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
649 
650     registerAsCurrent();
651 
652     bFirstCall = false;
653 
654     return true;
655 }
656 
CreateOpenGLContext()657 OpenGLContext* WinSalInstance::CreateOpenGLContext()
658 {
659     return new WinOpenGLContext;
660 }
661 
662 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
663