Cogs.Core
WGLContext.cpp
1#ifdef _WIN32
2
3#include "GLFuncPointers.h"
4#include "GLContext.h"
5
6#include "../IGraphicsDevice.h"
7#include "Foundation/Logging/Logger.h"
8#include "Foundation/Platform/WindowData.h"
9
10#include <cstring>
11
12namespace {
13
14 Cogs::Logging::Log logger = Cogs::Logging::getLogger("WGLContext");
15
16 PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = nullptr;
17 PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB = nullptr;
18 PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
19 PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = nullptr;
20 PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr;
21
22 struct LibFunc {
23 void** ptr;
24 const char* name;
25 };
26 LibFunc WGLFuncs[] = {
27 { (void**)&wglChoosePixelFormatARB, "wglChoosePixelFormatARB"},
28 { (void**)&wglGetPixelFormatAttribivARB, "wglGetPixelFormatAttribivARB"},
29 { (void**)&wglSwapIntervalEXT, "wglSwapIntervalEXT"},
30 { (void**)&wglGetExtensionsStringARB, "wglGetExtensionsStringARB"},
31 { (void**)&wglCreateContextAttribsARB, "wglCreateContextAttribsARB"},
32 };
33
34 bool getWGLFuncPointers()
35 {
36 for (const LibFunc& libFunc : WGLFuncs) {
37 *libFunc.ptr = wglGetProcAddress(libFunc.name);
38 if (*libFunc.ptr == nullptr) { // GetProcAddress signals failure by returning null
39 LOG_FATAL(logger, "Failed to look up %s (GetLastError()=%d).", libFunc.name, GetLastError());
40 return false;
41 }
42 }
43 return true;
44 }
45
59 LRESULT CALLBACK dummyWindowWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
60 {
61 if (message == WM_NCCREATE) {
62 HWND* hwndPtr = reinterpret_cast<HWND*>(((LPCREATESTRUCT)lParam)->lpCreateParams);
63 *hwndPtr = hWnd;
64 }
65 return ::DefWindowProc(hWnd, message, wParam, lParam);
66 }
67
68
69 bool dummyWindowCreate(HWND& hWnd, LPCWSTR windowName)
70 {
71 static ATOM classAtom = 0;
72 if (classAtom == 0) {
73 WNDCLASSEX wcex = { 0 };
74 wcex.cbSize = sizeof(WNDCLASSEX);
75 wcex.style = CS_OWNDC;
76 wcex.lpfnWndProc = dummyWindowWndProc;
77 wcex.hInstance = GetModuleHandle(NULL);
78 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
79 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2);
80 wcex.lpszClassName = TEXT("CogsRenderingWGLDummyWindow");
81 classAtom = RegisterClassEx(&wcex);
82 if (classAtom == 0) {
83 LOG_ERROR(logger, "RegisterClass failed for dummy window(GetLastError()=%x).", GetLastError());
84 return false;
85 }
86 }
87
88 CreateWindow((LPCTSTR)classAtom, windowName,
89 WS_OVERLAPPEDWINDOW,
90 CW_USEDEFAULT,
91 CW_USEDEFAULT,
92 CW_USEDEFAULT,
93 CW_USEDEFAULT,
94 NULL,
95 NULL,
96 GetModuleHandle(NULL),
97 &hWnd);
98
99 if (hWnd == nullptr) {
100 return false;
101 }
102 return true;
103 }
104
105 void dummyWindowDestroy(HWND& hwnd)
106 {
107 if (hwnd) {
108 DestroyWindow(hwnd);
109 hwnd = nullptr;
110 }
111 }
112
113 struct DummyContext
114 {
115 ~DummyContext() { destroy(); }
116
117 bool create()
118 {
119 if (!dummyWindowCreate(dummyWindow, TEXT("DummyContextWindow"))) return false;
120
121 PIXELFORMATDESCRIPTOR dummy_pfd = { 0 };
122 dummy_pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
123 dummy_pfd.nVersion = 1;
124 dummy_pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
125 dummy_pfd.iPixelType = PFD_TYPE_RGBA;
126 dummy_pfd.cColorBits = 32;
127 dummy_pfd.cDepthBits = 24;
128 dummy_pfd.cStencilBits = 8;
129 dummy_pfd.iLayerType = PFD_MAIN_PLANE;
130
131 hdc = GetDC(dummyWindow);
132 int dummy_pf = ChoosePixelFormat(hdc, &dummy_pfd);
133 if (!dummy_pf) {
134 LOG_FATAL(logger, "Failed to choose pixel format (GetLastError()=%d).", GetLastError());
135 return false;
136 }
137 if (!SetPixelFormat(hdc, dummy_pf, &dummy_pfd)) {
138 LOG_FATAL(logger, "Failed to set pixel format (GetLastError()=%d).", GetLastError());
139 return false;
140 }
141 context = wglCreateContext(hdc);
142 if (!context) {
143 LOG_FATAL(logger, "Failed to create dummy context (GetLastError()=%d).", GetLastError());
144 return false;
145 }
146
147 if (!wglMakeCurrent(hdc, context)) {
148 LOG_FATAL(logger, "Failed to make dummy context current (GetLastError()=%d).", GetLastError());
149 return false;
150 }
151
152 return true;
153 }
154
155 void destroy()
156 {
157 if (context) {
158 wglMakeCurrent(hdc, nullptr);
159 wglDeleteContext(context);
160 }
161 dummyWindowDestroy(dummyWindow);
162 }
163
164 HWND dummyWindow = nullptr;
165 HGLRC context = nullptr;
166 HDC hdc = nullptr;
167 };
168
169 bool chooseAndSetPixelFormat(HDC hdc, int requestedSampleCount, bool sRGB)
170 {
171 PIXELFORMATDESCRIPTOR pfd = { 0 };
172 std::vector<int> pfAttribs = {
173 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
174 WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
175 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
176 WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
177 WGL_COLOR_BITS_ARB, 24,
178 WGL_ALPHA_BITS_ARB, 8,
179 WGL_DEPTH_BITS_ARB, 24,
180 WGL_STENCIL_BITS_ARB, 8,
181 };
182
183 if (sRGB) {
184 pfAttribs.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB);
185 pfAttribs.push_back(GL_TRUE);
186 }
187 pfAttribs.push_back(0);
188
189 const int maxPixelFormats = 1024;
190 std::vector<int> pixelFormats(maxPixelFormats);
191 UINT numFormats;
192 if (!wglChoosePixelFormatARB(hdc, pfAttribs.data(), nullptr, maxPixelFormats, pixelFormats.data(), &numFormats))
193 {
194 LOG_FATAL(logger, "wglChoosePixelFormatARB failed (GetLastError()=%d).", GetLastError());
195 return false;
196 }
197
198 size_t bestFormat = 0;
199 int bestSampleCount = 0;
200 if (requestedSampleCount) {
201
202 static const int attributeNames[2] = { WGL_SAMPLE_BUFFERS_ARB, WGL_SAMPLES_ARB };
203 for (size_t i = 0; i < numFormats; i++) {
204 int attributeValues[2] = {};
205 if (!wglGetPixelFormatAttribivARB(hdc, pixelFormats[i], 0, 2, attributeNames, attributeValues)) {
206 LOG_FATAL(logger, "wglGetPixelFormatAttribivARB failed (GetLastError()=%d).", GetLastError());
207 return false;
208 }
209 int sampleBuffers = attributeValues[0];
210 int sampleCount = attributeValues[1];
211 if (sampleBuffers && (bestSampleCount < sampleCount) && (sampleCount <= requestedSampleCount)) {
212 bestFormat = i;
213 bestSampleCount = sampleCount;
214 }
215 LOG_DEBUG(logger, "%zu: format=0x%x sampleBufs=%d sampleCount=%d", i, pixelFormats[i], attributeValues[0], attributeValues[1]);
216 }
217 }
218 LOG_DEBUG(logger, "Requested %d samples, chose pixel format %zu: 0x%x", requestedSampleCount, bestFormat, pixelFormats[bestFormat]);
219
220 if (!SetPixelFormat(hdc, pixelFormats[bestFormat], &pfd)) {
221 LOG_FATAL(logger, "Failed to set pixel format (GetLastError()=%d).", GetLastError());
222 return false;
223 }
224 return true;
225 }
226
227
228 bool finalizeContext(Cogs::GLContextBase::Platform platform, bool debug, bool sRGB)
229 {
230 if (debug) {
231 Cogs::setUpGLDebugging(nullptr, platform);
232 }
233
234 if (sRGB) {
235 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
236 }
237
238 return true;
239 }
240
241}
242
243bool Cogs::GLContext::create(NativeDisplay /*display*/, WindowData* windowData, GLContext* shareContext, const GraphicsDeviceSettings* settings, Platform desiredPlatform, bool debug)
244{
245 assert(hdc == nullptr);
246 assert(context == nullptr);
247 platform = Platform::GL;
248
249 DummyContext dummyContext;
250 if (!dummyContext.create()) {
251 return false;
252 }
253 if (!getWGLFuncPointers()) {
254 LOG_FATAL(logger, "Failed to get required WGL function pointers");
255 return false;
256 }
257
258
259 if (windowData == nullptr) {
260 LOG_DEBUG(logger, "Creating headless dummy window");
261 if (!dummyWindowCreate(headlessHwnd, TEXT("HeadlessContextWindow"))) {
262 LOG_FATAL(logger, "Failed to create context sharing dummy window");
263 return false;
264 }
265 hdc = GetDC(headlessHwnd);
266 }
267 else {
268 hdc = GetDC(windowData->windowHandle);
269 }
270
271 const char* wglExtensions = wglGetExtensionsStringARB(hdc);
272 bool sRGB = std::strstr(wglExtensions, "WGL_EXT_framebuffer_sRGB") != nullptr;
273 int numSamples = settings && (std::strstr(wglExtensions, "WGL_ARB_multisample") != nullptr) ? settings->numSamples : 0;
274 if (!chooseAndSetPixelFormat(hdc, numSamples, sRGB)) {
275 return false;
276 }
277
278 std::vector<int> cxAttribs;
279 if (debug) {
280 cxAttribs.push_back(WGL_CONTEXT_FLAGS_ARB); cxAttribs.push_back(WGL_CONTEXT_DEBUG_BIT_ARB);
281 }
282
283 // OpenGL ES3 - try to get ES context
284 if (desiredPlatform == Platform::ES3) {
285 if (std::strstr(wglExtensions, "WGL_EXT_create_context_es2_profile") != nullptr) {
286 LOG_DEBUG(logger, "WGL_EXT_create_context_es2_profile present, creating real ES3 context");
287 cxAttribs.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); cxAttribs.push_back(WGL_CONTEXT_ES2_PROFILE_BIT_EXT);
288 cxAttribs.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); cxAttribs.push_back(3);
289 cxAttribs.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); cxAttribs.push_back(0);
290 platform = Platform::ES3;
291 }
292 else {
293 platform = Platform::GL;
294 }
295 }
296
297 // OpenGL Desktop
298 else {
299 // Note: 3.0 will give you most recent version with compatibility
300 // From 3.1 it will in practice give you that specific version
301 cxAttribs.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB); cxAttribs.push_back(3);
302 cxAttribs.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); cxAttribs.push_back(0);
303 platform = Platform::GL;
304 }
305 cxAttribs.push_back(0);
306
307 context = wglCreateContextAttribsARB(hdc, shareContext ? shareContext->context : nullptr, cxAttribs.data());
308 if (!context) {
309 LOG_ERROR(logger, "wglCreateContextAttribsARB failed (GetLastError()=%d).", GetLastError());
310 return false;
311 }
312
313
314 dummyContext.destroy();
315
316 if (!makeCurrent()) {
317 LOG_ERROR(logger, "Failed to make context current (GetLastError()=%d).", GetLastError());
318 return false;
319 }
320
321 // OpenGL ES3
322 if (desiredPlatform == Platform::ES3) {
323 if (!getGLES3FuncPointers(platform)) {
324 destroy();
325 return false;
326 }
327 if (platform == Platform::GL && (std::strstr((const char*)glGetString(GL_EXTENSIONS), "ARB_ES3_compatibility") == nullptr)) {
328 LOG_FATAL(logger, "Neither WGL_EXT_create_context_es2_profile nor ARB_ES3_compatibility extensions are present");
329 destroy();
330 return false;
331 }
332 }
333
334 // OpenGL Desktop
335 else {
336 if (!getGLFuncPointers()) {
337 destroy();
338 return false;
339 }
340 }
341
342 finalizeContext(platform, debug, sRGB);
343 return true;
344}
345
346bool Cogs::GLContext::makeCurrent()
347{
348 return wglMakeCurrent(hdc, context);
349}
350
351void Cogs::GLContext::swapBuffers(uint32_t syncInterval, uint32_t presentFlags)
352{
353
354 if (context == 0) return;
355
356 if ((presentFlags & PresentFlags::NoSwap) == 0) {
357 if (wglSwapIntervalEXT) {
358 wglSwapIntervalEXT(syncInterval != 0 ? 1 : 0);
359 }
360 ::SwapBuffers(hdc);
361 }
362
363}
364
365
366void Cogs::GLContext::destroy()
367{
368 if (context) {
369 wglMakeCurrent(hdc, nullptr);
370 wglDeleteContext(context);
371 context = nullptr;
372 }
373 hdc = nullptr;
374}
375
376#endif
Log implementation class.
Definition: LogManager.h:139
@ sRGB
Value is a color and is subject to gamma correction.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180