Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Windows/GPU/WindowsGLContext.cpp
3185 views
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 "Core/System.h"
32
#include "Common/Data/Encoding/Utf8.h"
33
#include "Common/Data/Text/I18n.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
g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D11;
274
g_Config.sFailedGPUBackends.clear();
275
g_Config.Save("save_d3d11_fallback");
276
277
W32Util::ExitAndRestart();
278
}
279
280
// Avoid further error messages. Let's just bail, it's safe, and we can't continue.
281
ExitProcess(1);
282
}
283
284
// Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc.
285
// glewExperimental allows us to force GLEW to search for the pointers anyway.
286
glewExperimental = true;
287
if (GLEW_OK != glewInit()) {
288
*error_message = "Failed to initialize GLEW.";
289
return false;
290
}
291
// Unfortunately, glew will generate an invalid enum error, ignore.
292
glGetError();
293
294
// Reset in case we're in a backend switch.
295
ResetGLExtensions();
296
297
int contextFlags = g_Config.bGfxDebugOutput ? WGL_CONTEXT_DEBUG_BIT_ARB : 0;
298
299
HGLRC m_hrc = nullptr;
300
// Alright, now for the modernity. First try a 4.4, then 4.3, context, if that fails try 3.3.
301
// I can't seem to find a way that lets you simply request the newest version available.
302
if (wglewIsSupported("WGL_ARB_create_context") == 1) {
303
if (simulateGLES) {
304
const static int simulateVersions[][2] = { {3, 2}, {3, 1}, {3, 0}, {2, 0} };
305
for (auto ver : simulateVersions) {
306
const int attribsES[] = {
307
WGL_CONTEXT_MAJOR_VERSION_ARB, ver[0],
308
WGL_CONTEXT_MINOR_VERSION_ARB, ver[1],
309
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_ES2_PROFILE_BIT_EXT,
310
0
311
};
312
m_hrc = wglCreateContextAttribsARB(hDC, 0, attribsES);
313
if (m_hrc)
314
break;
315
}
316
}
317
318
for (int tryCore = 1; tryCore >= 0 && m_hrc == nullptr; --tryCore) {
319
SetGLCoreContext(tryCore == 1);
320
321
for (int minor = 6; minor >= 0 && m_hrc == nullptr; --minor) {
322
const int attribs4x[] = {
323
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
324
WGL_CONTEXT_MINOR_VERSION_ARB, minor,
325
WGL_CONTEXT_FLAGS_ARB, contextFlags,
326
WGL_CONTEXT_PROFILE_MASK_ARB, gl_extensions.IsCoreContext ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
327
0
328
};
329
m_hrc = wglCreateContextAttribsARB(hDC, 0, attribs4x);
330
}
331
const int attribs33[] = {
332
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
333
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
334
WGL_CONTEXT_FLAGS_ARB, contextFlags,
335
WGL_CONTEXT_PROFILE_MASK_ARB, gl_extensions.IsCoreContext ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
336
0
337
};
338
if (!m_hrc)
339
m_hrc = wglCreateContextAttribsARB(hDC, 0, attribs33);
340
}
341
342
if (!m_hrc) {
343
// Fall back
344
m_hrc = hRC;
345
} else {
346
// Switch to the new ARB context.
347
wglMakeCurrent(nullptr, nullptr);
348
wglDeleteContext(hRC);
349
wglMakeCurrent(hDC, m_hrc);
350
}
351
} else {
352
// We can't make a GL 3.x context. Use an old style context (GL 2.1 and before)
353
m_hrc = hRC;
354
}
355
356
if (GLEW_OK != glewInit()) {
357
*error_message = "Failed to re-initialize GLEW.";
358
return false;
359
}
360
// Unfortunately, glew will generate an invalid enum error, ignore.
361
if (gl_extensions.IsCoreContext)
362
glGetError();
363
364
if (!m_hrc) {
365
*error_message = "No m_hrc";
366
return false;
367
}
368
369
hRC = m_hrc;
370
371
bool validation = g_Config.bGfxDebugOutput;
372
373
// Always run OpenGL validation in debug mode, just like we do with Vulkan if debug layers are installed.
374
#ifdef _DEBUG
375
validation = true;
376
#endif
377
378
if (validation) {
379
if (wglewIsSupported("GL_KHR_debug") == 1) {
380
glGetError();
381
glDebugMessageCallback((GLDEBUGPROC)&DebugCallbackARB, nullptr);
382
if (glGetError()) {
383
ERROR_LOG(Log::G3D, "Failed to register a debug log callback");
384
}
385
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
386
if (glGetError()) {
387
ERROR_LOG(Log::G3D, "Failed to enable synchronous debug output");
388
}
389
} else if (glewIsSupported("GL_ARB_debug_output")) {
390
glGetError();
391
glDebugMessageCallbackARB((GLDEBUGPROCARB)&DebugCallbackARB, 0); // print debug output to stderr
392
if (glGetError()) {
393
ERROR_LOG(Log::G3D, "Failed to register a debug log callback");
394
}
395
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
396
if (glGetError()) {
397
ERROR_LOG(Log::G3D, "Failed to enable synchronous debug output");
398
}
399
400
// For extra verbosity uncomment this (MEDIUM and HIGH are on by default):
401
// glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, nullptr, GL_TRUE);
402
}
403
404
glEnable(GL_DEBUG_OUTPUT);
405
}
406
407
pauseRequested = false;
408
resumeRequested = false;
409
410
CheckGLExtensions();
411
draw_ = Draw::T3DCreateGLContext(wglSwapIntervalEXT != nullptr);
412
bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these.
413
if (!success) {
414
delete draw_;
415
draw_ = nullptr;
416
ReleaseGLContext();
417
return false;
418
}
419
420
draw_->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) {
421
g_OSD.Show(OSDType::MESSAGE_ERROR, details, 0.0f, "error_callback");
422
}, nullptr);
423
424
// These are auto-reset events.
425
pauseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
426
resumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
427
428
renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
429
renderManager_->SetInflightFrames(g_Config.iInflightFrames);
430
SetGPUBackend(GPUBackend::OPENGL);
431
renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); });
432
if (wglSwapIntervalEXT) {
433
// glew loads wglSwapIntervalEXT if available
434
renderManager_->SetSwapIntervalFunction([&](int interval) {
435
wglSwapIntervalEXT(interval);
436
});
437
}
438
CHECK_GL_ERROR_IF_DEBUG();
439
return true; // Success
440
}
441
442
void WindowsGLContext::Shutdown() {
443
glslang::FinalizeProcess();
444
}
445
446
void WindowsGLContext::ReleaseGLContext() {
447
if (hRC) {
448
// Are we able to release the DC and RC contexts?
449
if (!wglMakeCurrent(NULL, NULL)) {
450
MessageBox(NULL, L"Release of DC and RC failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
451
}
452
453
// Are we able to delete the RC?
454
if (!wglDeleteContext(hRC)) {
455
MessageBox(NULL, L"Release rendering context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
456
}
457
hRC = NULL;
458
}
459
460
if (hDC && !ReleaseDC(hWnd_, hDC)) {
461
DWORD err = GetLastError();
462
if (err != ERROR_DC_NOT_FOUND) {
463
MessageBox(NULL, L"Release device context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
464
}
465
hDC = NULL;
466
}
467
hWnd_ = NULL;
468
}
469
470
void WindowsGLContext::ShutdownFromRenderThread() {
471
delete draw_;
472
draw_ = nullptr;
473
CloseHandle(pauseEvent);
474
CloseHandle(resumeEvent);
475
ReleaseGLContext();
476
}
477
478
void WindowsGLContext::Resize() {
479
}
480
481
void WindowsGLContext::ThreadStart() {
482
renderManager_->ThreadStart(draw_);
483
}
484
485
bool WindowsGLContext::ThreadFrame() {
486
return renderManager_->ThreadFrame();
487
}
488
489
void WindowsGLContext::ThreadEnd() {
490
renderManager_->ThreadEnd();
491
}
492
493
void WindowsGLContext::StopThread() {
494
renderManager_->StopThread();
495
}
496
497