CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/GPU/WindowsGLContext.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "Common/Log.h"
19
#include "Common/CommonWindows.h"
20
#include "Common/GPU/OpenGL/GLCommon.h"
21
#include "Common/GPU/OpenGL/GLDebugLog.h"
22
#include "Common/GPU/OpenGL/GLFeatures.h"
23
#include "Common/GPU/thin3d_create.h"
24
#include "Common/GPU/OpenGL/GLRenderManager.h"
25
#include "Common/System/OSD.h"
26
#include "GL/gl.h"
27
#include "GL/wglew.h"
28
#include "Core/Config.h"
29
#include "Core/ConfigValues.h"
30
#include "Core/Core.h"
31
#include "Common/Data/Encoding/Utf8.h"
32
#include "Common/Data/Text/I18n.h"
33
#include "UI/OnScreenDisplay.h"
34
#include "ext/glslang/glslang/Public/ShaderLang.h"
35
36
#include "Windows/W32Util/Misc.h"
37
#include "Windows/GPU/WindowsGLContext.h"
38
39
// Currently, just compile time for debugging. May be NVIDIA only.
40
static const int simulateGLES = false;
41
42
void WindowsGLContext::Poll() {
43
// We no longer call RenderManager::Swap here, it's handled by the render thread, which
44
// we're not on here.
45
46
// Used during fullscreen switching to prevent rendering.
47
if (pauseRequested) {
48
SetEvent(pauseEvent);
49
resumeRequested = true;
50
DWORD result = WaitForSingleObject(resumeEvent, INFINITE);
51
if (result == WAIT_TIMEOUT) {
52
ERROR_LOG(Log::G3D, "Wait for resume timed out. Resuming rendering");
53
}
54
pauseRequested = false;
55
}
56
}
57
58
void WindowsGLContext::Pause() {
59
if (!hRC) {
60
return;
61
}
62
if (Core_IsStepping()) {
63
return;
64
}
65
66
pauseRequested = true;
67
DWORD result = WaitForSingleObject(pauseEvent, INFINITE);
68
if (result == WAIT_TIMEOUT) {
69
ERROR_LOG(Log::G3D, "Wait for pause timed out");
70
}
71
// OK, we now know the rendering thread is paused.
72
}
73
74
void WindowsGLContext::Resume() {
75
if (!hRC) {
76
return;
77
}
78
if (Core_IsStepping() && !resumeRequested) {
79
return;
80
}
81
82
if (!resumeRequested) {
83
ERROR_LOG(Log::G3D, "Not waiting to get resumed");
84
} else {
85
SetEvent(resumeEvent);
86
}
87
resumeRequested = false;
88
}
89
90
void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenum type,
91
GLuint id, GLenum severity, const char *msg) {
92
93
char sourceStr[32]{};
94
const char *sourceFmt;
95
switch(source) {
96
case GL_DEBUG_SOURCE_API_ARB: sourceFmt = "API"; break;
97
case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: sourceFmt = "WINDOW_SYSTEM"; break;
98
case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: sourceFmt = "SHADER_COMPILER"; break;
99
case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: sourceFmt = "THIRD_PARTY"; break;
100
case GL_DEBUG_SOURCE_APPLICATION_ARB: sourceFmt = "APPLICATION"; break;
101
case GL_DEBUG_SOURCE_OTHER_ARB: sourceFmt = "OTHER"; break;
102
default: sourceFmt = "UNDEFINED(0x%04X)"; break;
103
}
104
snprintf(sourceStr, sizeof(sourceStr), sourceFmt, source);
105
106
char typeStr[32]{};
107
const char *typeFmt;
108
switch(type) {
109
case GL_DEBUG_TYPE_ERROR_ARB: typeFmt = "ERROR"; break;
110
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: typeFmt = "DEPRECATED_BEHAVIOR"; break;
111
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: typeFmt = "UNDEFINED_BEHAVIOR"; break;
112
case GL_DEBUG_TYPE_PORTABILITY_ARB: typeFmt = "PORTABILITY"; break;
113
case GL_DEBUG_TYPE_PERFORMANCE_ARB: typeFmt = "PERFORMANCE"; break;
114
case GL_DEBUG_TYPE_OTHER_ARB: typeFmt = "OTHER"; break;
115
default: typeFmt = "UNDEFINED(0x%04X)"; break;
116
}
117
snprintf(typeStr, sizeof(typeStr), typeFmt, type);
118
119
char severityStr[32]{};
120
const char *severityFmt;
121
switch (severity) {
122
case GL_DEBUG_SEVERITY_HIGH_ARB: severityFmt = "HIGH"; break;
123
case GL_DEBUG_SEVERITY_MEDIUM_ARB: severityFmt = "MEDIUM"; break;
124
case GL_DEBUG_SEVERITY_LOW_ARB: severityFmt = "LOW"; break;
125
default: severityFmt = "UNDEFINED(%d)"; break;
126
}
127
128
snprintf(severityStr, sizeof(severityStr), severityFmt, severity);
129
snprintf(outStr, outStrSize, "OpenGL: %s [source=%s type=%s severity=%s id=%d]\n", msg, sourceStr, typeStr, severityStr, id);
130
}
131
132
void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity,
133
GLsizei length, const GLchar *message, GLvoid *userParam) {
134
// Ignore buffer mapping messages from NVIDIA
135
if (source == GL_DEBUG_SOURCE_API_ARB && type == GL_DEBUG_TYPE_OTHER_ARB && id == 131185) {
136
return;
137
}
138
// Ignore application messages
139
if (source == GL_DEBUG_SOURCE_APPLICATION) {
140
return;
141
}
142
143
(void)length;
144
FILE *outFile = (FILE *)userParam;
145
char finalMessage[1024];
146
FormatDebugOutputARB(finalMessage, sizeof(finalMessage), source, type, id, severity, message);
147
OutputDebugStringA(finalMessage);
148
149
// Truncate the \n before passing to our log functions.
150
size_t len = strlen(finalMessage);
151
if (len) {
152
finalMessage[len - 1] = '\0';
153
}
154
155
switch (type) {
156
case GL_DEBUG_TYPE_ERROR_ARB:
157
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
158
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
159
ERROR_LOG(Log::G3D, "GL: %s", finalMessage);
160
break;
161
162
case GL_DEBUG_TYPE_PORTABILITY_ARB:
163
case GL_DEBUG_TYPE_PERFORMANCE_ARB:
164
NOTICE_LOG(Log::G3D, "GL: %s", finalMessage);
165
break;
166
167
case GL_DEBUG_TYPE_OTHER_ARB:
168
default:
169
// These are just performance warnings.
170
VERBOSE_LOG(Log::G3D, "GL: %s", finalMessage);
171
break;
172
}
173
}
174
175
bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_message) {
176
glslang::InitializeProcess();
177
178
hInst_ = hInst;
179
hWnd_ = window;
180
*error_message = "ok";
181
return true;
182
}
183
184
bool WindowsGLContext::InitFromRenderThread(std::string *error_message) {
185
*error_message = "ok";
186
GLuint PixelFormat;
187
188
// TODO: Change to use WGL_ARB_pixel_format instead
189
static const PIXELFORMATDESCRIPTOR pfd = {
190
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
191
1, // Version Number
192
PFD_DRAW_TO_WINDOW | // Format Must Support Window
193
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
194
PFD_DOUBLEBUFFER, // Must Support Double Buffering
195
PFD_TYPE_RGBA, // Request An RGBA Format
196
24, // Select Our Color Depth
197
0, 0, 0, 0, 0, 0, // Color Bits Ignored
198
8, // No Alpha Buffer
199
0, // Shift Bit Ignored
200
0, // No Accumulation Buffer
201
0, 0, 0, 0, // Accumulation Bits Ignored
202
16, // At least a 16Bit Z-Buffer (Depth Buffer)
203
8, // 8-bit Stencil Buffer
204
0, // No Auxiliary Buffer
205
PFD_MAIN_PLANE, // Main Drawing Layer
206
0, // Reserved
207
0, 0, 0 // Layer Masks Ignored
208
};
209
210
hDC = GetDC(hWnd_);
211
212
if (!hDC) {
213
*error_message = "Failed to get a device context.";
214
return false; // Return FALSE
215
}
216
217
if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) {
218
*error_message = "Can't find a suitable PixelFormat.";
219
return false;
220
}
221
222
if (!SetPixelFormat(hDC, PixelFormat, &pfd)) {
223
*error_message = "Can't set the PixelFormat.";
224
return false;
225
}
226
227
if (!(hRC = wglCreateContext(hDC))) {
228
*error_message = "Can't create a GL rendering context.";
229
return false;
230
}
231
232
if (!wglMakeCurrent(hDC, hRC)) {
233
*error_message = "Can't activate the GL rendering context.";
234
return false;
235
}
236
237
// Check for really old OpenGL drivers and refuse to run really early in some cases.
238
239
// TODO: Also either tell the user to give up or point the user to the right websites. Here's some collected
240
// information about a system that will not work:
241
242
// GL_VERSION GL_VENDOR GL_RENDERER
243
// "1.4.0 - Build 8.14.10.2364" "intel" intel Pineview Platform
244
std::string glVersion = (const char *)glGetString(GL_VERSION);
245
std::string glRenderer = (const char *)glGetString(GL_RENDERER);
246
const std::string openGL_1 = "1.";
247
248
if (glRenderer == "GDI Generic" || glVersion.substr(0, openGL_1.size()) == openGL_1) {
249
//The error may come from 16-bit colour mode
250
//Check Colour depth
251
HDC dc = GetDC(NULL);
252
u32 colour_depth = GetDeviceCaps(dc, BITSPIXEL);
253
ReleaseDC(NULL, dc);
254
if (colour_depth != 32) {
255
MessageBox(0, L"Please switch your display to 32-bit colour mode", L"OpenGL Error", MB_OK);
256
ExitProcess(1);
257
}
258
const char *defaultError = "Insufficient OpenGL driver support detected!\n\n"
259
"Your GPU reports that it does not support OpenGL 2.0. Would you like to try using DirectX instead?\n\n"
260
"DirectX is currently compatible with less games, but on your GPU it may be the only choice.\n\n"
261
"Visit the forums at https://forums.ppsspp.org for more information.\n\n";
262
263
auto err = GetI18NCategory(I18NCat::ERRORS);
264
std::wstring versionDetected = ConvertUTF8ToWString(glVersion + "\n\n");
265
std::wstring error = ConvertUTF8ToWString(err->T("InsufficientOpenGLDriver", defaultError));
266
std::wstring title = ConvertUTF8ToWString(err->T("OpenGLDriverError", "OpenGL driver error"));
267
std::wstring combined = versionDetected + error;
268
269
bool yes = IDYES == MessageBox(hWnd_, combined.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO);
270
271
if (yes) {
272
// Change the config to D3D and restart.
273
const char *d3d9Or11 = "Direct3D 9? (Or no for Direct3D 11)";
274
std::wstring whichD3D9 = ConvertUTF8ToWString(err->T("D3D9or11", d3d9Or11));
275
bool d3d9 = IDYES == MessageBox(hWnd_, whichD3D9.c_str(), title.c_str(), MB_YESNO);
276
g_Config.iGPUBackend = d3d9 ? (int)GPUBackend::DIRECT3D9 : (int)GPUBackend::DIRECT3D11;
277
g_Config.sFailedGPUBackends.clear();
278
g_Config.Save("save_d3d11_fallback");
279
280
W32Util::ExitAndRestart();
281
}
282
283
// Avoid further error messages. Let's just bail, it's safe, and we can't continue.
284
ExitProcess(1);
285
}
286
287
// Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc.
288
// glewExperimental allows us to force GLEW to search for the pointers anyway.
289
glewExperimental = true;
290
if (GLEW_OK != glewInit()) {
291
*error_message = "Failed to initialize GLEW.";
292
return false;
293
}
294
// Unfortunately, glew will generate an invalid enum error, ignore.
295
glGetError();
296
297
// Reset in case we're in a backend switch.
298
ResetGLExtensions();
299
300
int contextFlags = g_Config.bGfxDebugOutput ? WGL_CONTEXT_DEBUG_BIT_ARB : 0;
301
302
HGLRC m_hrc = nullptr;
303
// Alright, now for the modernity. First try a 4.4, then 4.3, context, if that fails try 3.3.
304
// I can't seem to find a way that lets you simply request the newest version available.
305
if (wglewIsSupported("WGL_ARB_create_context") == 1) {
306
if (simulateGLES) {
307
const static int simulateVersions[][2] = { {3, 2}, {3, 1}, {3, 0}, {2, 0} };
308
for (auto ver : simulateVersions) {
309
const int attribsES[] = {
310
WGL_CONTEXT_MAJOR_VERSION_ARB, ver[0],
311
WGL_CONTEXT_MINOR_VERSION_ARB, ver[1],
312
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_ES2_PROFILE_BIT_EXT,
313
0
314
};
315
m_hrc = wglCreateContextAttribsARB(hDC, 0, attribsES);
316
if (m_hrc)
317
break;
318
}
319
}
320
321
for (int tryCore = 1; tryCore >= 0 && m_hrc == nullptr; --tryCore) {
322
SetGLCoreContext(tryCore == 1);
323
324
for (int minor = 6; minor >= 0 && m_hrc == nullptr; --minor) {
325
const int attribs4x[] = {
326
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
327
WGL_CONTEXT_MINOR_VERSION_ARB, minor,
328
WGL_CONTEXT_FLAGS_ARB, contextFlags,
329
WGL_CONTEXT_PROFILE_MASK_ARB, gl_extensions.IsCoreContext ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
330
0
331
};
332
m_hrc = wglCreateContextAttribsARB(hDC, 0, attribs4x);
333
}
334
const int attribs33[] = {
335
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
336
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
337
WGL_CONTEXT_FLAGS_ARB, contextFlags,
338
WGL_CONTEXT_PROFILE_MASK_ARB, gl_extensions.IsCoreContext ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
339
0
340
};
341
if (!m_hrc)
342
m_hrc = wglCreateContextAttribsARB(hDC, 0, attribs33);
343
}
344
345
if (!m_hrc) {
346
// Fall back
347
m_hrc = hRC;
348
} else {
349
// Switch to the new ARB context.
350
wglMakeCurrent(nullptr, nullptr);
351
wglDeleteContext(hRC);
352
wglMakeCurrent(hDC, m_hrc);
353
}
354
} else {
355
// We can't make a GL 3.x context. Use an old style context (GL 2.1 and before)
356
m_hrc = hRC;
357
}
358
359
if (GLEW_OK != glewInit()) {
360
*error_message = "Failed to re-initialize GLEW.";
361
return false;
362
}
363
// Unfortunately, glew will generate an invalid enum error, ignore.
364
if (gl_extensions.IsCoreContext)
365
glGetError();
366
367
if (!m_hrc) {
368
*error_message = "No m_hrc";
369
return false;
370
}
371
372
hRC = m_hrc;
373
374
bool validation = g_Config.bGfxDebugOutput;
375
376
// Always run OpenGL validation in debug mode, just like we do with Vulkan if debug layers are installed.
377
#ifdef _DEBUG
378
validation = true;
379
#endif
380
381
if (validation) {
382
if (wglewIsSupported("GL_KHR_debug") == 1) {
383
glGetError();
384
glDebugMessageCallback((GLDEBUGPROC)&DebugCallbackARB, nullptr);
385
if (glGetError()) {
386
ERROR_LOG(Log::G3D, "Failed to register a debug log callback");
387
}
388
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
389
if (glGetError()) {
390
ERROR_LOG(Log::G3D, "Failed to enable synchronous debug output");
391
}
392
} else if (glewIsSupported("GL_ARB_debug_output")) {
393
glGetError();
394
glDebugMessageCallbackARB((GLDEBUGPROCARB)&DebugCallbackARB, 0); // print debug output to stderr
395
if (glGetError()) {
396
ERROR_LOG(Log::G3D, "Failed to register a debug log callback");
397
}
398
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
399
if (glGetError()) {
400
ERROR_LOG(Log::G3D, "Failed to enable synchronous debug output");
401
}
402
403
// For extra verbosity uncomment this (MEDIUM and HIGH are on by default):
404
// glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, nullptr, GL_TRUE);
405
}
406
407
glEnable(GL_DEBUG_OUTPUT);
408
}
409
410
pauseRequested = false;
411
resumeRequested = false;
412
413
CheckGLExtensions();
414
draw_ = Draw::T3DCreateGLContext(wglSwapIntervalEXT != nullptr);
415
bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these.
416
if (!success) {
417
delete draw_;
418
draw_ = nullptr;
419
ReleaseGLContext();
420
return false;
421
}
422
423
draw_->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) {
424
g_OSD.Show(OSDType::MESSAGE_ERROR, details, 0.0f, "error_callback");
425
}, nullptr);
426
427
// These are auto-reset events.
428
pauseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
429
resumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
430
431
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
432
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
433
SetGPUBackend(GPUBackend::OPENGL);
434
renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); });
435
if (wglSwapIntervalEXT) {
436
// glew loads wglSwapIntervalEXT if available
437
renderManager_->SetSwapIntervalFunction([&](int interval) {
438
wglSwapIntervalEXT(interval);
439
});
440
}
441
CHECK_GL_ERROR_IF_DEBUG();
442
return true; // Success
443
}
444
445
void WindowsGLContext::Shutdown() {
446
glslang::FinalizeProcess();
447
}
448
449
void WindowsGLContext::ReleaseGLContext() {
450
if (hRC) {
451
// Are we able to release the DC and RC contexts?
452
if (!wglMakeCurrent(NULL, NULL)) {
453
MessageBox(NULL, L"Release of DC and RC failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
454
}
455
456
// Are we able to delete the RC?
457
if (!wglDeleteContext(hRC)) {
458
MessageBox(NULL, L"Release rendering context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
459
}
460
hRC = NULL;
461
}
462
463
if (hDC && !ReleaseDC(hWnd_, hDC)) {
464
DWORD err = GetLastError();
465
if (err != ERROR_DC_NOT_FOUND) {
466
MessageBox(NULL, L"Release device context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
467
}
468
hDC = NULL;
469
}
470
hWnd_ = NULL;
471
}
472
473
void WindowsGLContext::ShutdownFromRenderThread() {
474
delete draw_;
475
draw_ = nullptr;
476
CloseHandle(pauseEvent);
477
CloseHandle(resumeEvent);
478
ReleaseGLContext();
479
}
480
481
void WindowsGLContext::Resize() {
482
}
483
484
void WindowsGLContext::ThreadStart() {
485
renderManager_->ThreadStart(draw_);
486
}
487
488
bool WindowsGLContext::ThreadFrame() {
489
return renderManager_->ThreadFrame();
490
}
491
492
void WindowsGLContext::ThreadEnd() {
493
renderManager_->ThreadEnd();
494
}
495
496
void WindowsGLContext::StopThread() {
497
renderManager_->StopThread();
498
}
499
500