CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Windows/GPU/WindowsGLContext.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "Common/Log.h"18#include "Common/CommonWindows.h"19#include "Common/GPU/OpenGL/GLCommon.h"20#include "Common/GPU/OpenGL/GLDebugLog.h"21#include "Common/GPU/OpenGL/GLFeatures.h"22#include "Common/GPU/thin3d_create.h"23#include "Common/GPU/OpenGL/GLRenderManager.h"24#include "Common/System/OSD.h"25#include "GL/gl.h"26#include "GL/wglew.h"27#include "Core/Config.h"28#include "Core/ConfigValues.h"29#include "Core/Core.h"30#include "Common/Data/Encoding/Utf8.h"31#include "Common/Data/Text/I18n.h"32#include "UI/OnScreenDisplay.h"33#include "ext/glslang/glslang/Public/ShaderLang.h"3435#include "Windows/W32Util/Misc.h"36#include "Windows/GPU/WindowsGLContext.h"3738// Currently, just compile time for debugging. May be NVIDIA only.39static const int simulateGLES = false;4041void WindowsGLContext::Poll() {42// We no longer call RenderManager::Swap here, it's handled by the render thread, which43// we're not on here.4445// Used during fullscreen switching to prevent rendering.46if (pauseRequested) {47SetEvent(pauseEvent);48resumeRequested = true;49DWORD result = WaitForSingleObject(resumeEvent, INFINITE);50if (result == WAIT_TIMEOUT) {51ERROR_LOG(Log::G3D, "Wait for resume timed out. Resuming rendering");52}53pauseRequested = false;54}55}5657void WindowsGLContext::Pause() {58if (!hRC) {59return;60}61if (Core_IsStepping()) {62return;63}6465pauseRequested = true;66DWORD result = WaitForSingleObject(pauseEvent, INFINITE);67if (result == WAIT_TIMEOUT) {68ERROR_LOG(Log::G3D, "Wait for pause timed out");69}70// OK, we now know the rendering thread is paused.71}7273void WindowsGLContext::Resume() {74if (!hRC) {75return;76}77if (Core_IsStepping() && !resumeRequested) {78return;79}8081if (!resumeRequested) {82ERROR_LOG(Log::G3D, "Not waiting to get resumed");83} else {84SetEvent(resumeEvent);85}86resumeRequested = false;87}8889void FormatDebugOutputARB(char outStr[], size_t outStrSize, GLenum source, GLenum type,90GLuint id, GLenum severity, const char *msg) {9192char sourceStr[32]{};93const char *sourceFmt;94switch(source) {95case GL_DEBUG_SOURCE_API_ARB: sourceFmt = "API"; break;96case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: sourceFmt = "WINDOW_SYSTEM"; break;97case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: sourceFmt = "SHADER_COMPILER"; break;98case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: sourceFmt = "THIRD_PARTY"; break;99case GL_DEBUG_SOURCE_APPLICATION_ARB: sourceFmt = "APPLICATION"; break;100case GL_DEBUG_SOURCE_OTHER_ARB: sourceFmt = "OTHER"; break;101default: sourceFmt = "UNDEFINED(0x%04X)"; break;102}103snprintf(sourceStr, sizeof(sourceStr), sourceFmt, source);104105char typeStr[32]{};106const char *typeFmt;107switch(type) {108case GL_DEBUG_TYPE_ERROR_ARB: typeFmt = "ERROR"; break;109case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: typeFmt = "DEPRECATED_BEHAVIOR"; break;110case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: typeFmt = "UNDEFINED_BEHAVIOR"; break;111case GL_DEBUG_TYPE_PORTABILITY_ARB: typeFmt = "PORTABILITY"; break;112case GL_DEBUG_TYPE_PERFORMANCE_ARB: typeFmt = "PERFORMANCE"; break;113case GL_DEBUG_TYPE_OTHER_ARB: typeFmt = "OTHER"; break;114default: typeFmt = "UNDEFINED(0x%04X)"; break;115}116snprintf(typeStr, sizeof(typeStr), typeFmt, type);117118char severityStr[32]{};119const char *severityFmt;120switch (severity) {121case GL_DEBUG_SEVERITY_HIGH_ARB: severityFmt = "HIGH"; break;122case GL_DEBUG_SEVERITY_MEDIUM_ARB: severityFmt = "MEDIUM"; break;123case GL_DEBUG_SEVERITY_LOW_ARB: severityFmt = "LOW"; break;124default: severityFmt = "UNDEFINED(%d)"; break;125}126127snprintf(severityStr, sizeof(severityStr), severityFmt, severity);128snprintf(outStr, outStrSize, "OpenGL: %s [source=%s type=%s severity=%s id=%d]\n", msg, sourceStr, typeStr, severityStr, id);129}130131void DebugCallbackARB(GLenum source, GLenum type, GLuint id, GLenum severity,132GLsizei length, const GLchar *message, GLvoid *userParam) {133// Ignore buffer mapping messages from NVIDIA134if (source == GL_DEBUG_SOURCE_API_ARB && type == GL_DEBUG_TYPE_OTHER_ARB && id == 131185) {135return;136}137// Ignore application messages138if (source == GL_DEBUG_SOURCE_APPLICATION) {139return;140}141142(void)length;143FILE *outFile = (FILE *)userParam;144char finalMessage[1024];145FormatDebugOutputARB(finalMessage, sizeof(finalMessage), source, type, id, severity, message);146OutputDebugStringA(finalMessage);147148// Truncate the \n before passing to our log functions.149size_t len = strlen(finalMessage);150if (len) {151finalMessage[len - 1] = '\0';152}153154switch (type) {155case GL_DEBUG_TYPE_ERROR_ARB:156case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:157case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:158ERROR_LOG(Log::G3D, "GL: %s", finalMessage);159break;160161case GL_DEBUG_TYPE_PORTABILITY_ARB:162case GL_DEBUG_TYPE_PERFORMANCE_ARB:163NOTICE_LOG(Log::G3D, "GL: %s", finalMessage);164break;165166case GL_DEBUG_TYPE_OTHER_ARB:167default:168// These are just performance warnings.169VERBOSE_LOG(Log::G3D, "GL: %s", finalMessage);170break;171}172}173174bool WindowsGLContext::Init(HINSTANCE hInst, HWND window, std::string *error_message) {175glslang::InitializeProcess();176177hInst_ = hInst;178hWnd_ = window;179*error_message = "ok";180return true;181}182183bool WindowsGLContext::InitFromRenderThread(std::string *error_message) {184*error_message = "ok";185GLuint PixelFormat;186187// TODO: Change to use WGL_ARB_pixel_format instead188static const PIXELFORMATDESCRIPTOR pfd = {189sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor1901, // Version Number191PFD_DRAW_TO_WINDOW | // Format Must Support Window192PFD_SUPPORT_OPENGL | // Format Must Support OpenGL193PFD_DOUBLEBUFFER, // Must Support Double Buffering194PFD_TYPE_RGBA, // Request An RGBA Format19524, // Select Our Color Depth1960, 0, 0, 0, 0, 0, // Color Bits Ignored1978, // No Alpha Buffer1980, // Shift Bit Ignored1990, // No Accumulation Buffer2000, 0, 0, 0, // Accumulation Bits Ignored20116, // At least a 16Bit Z-Buffer (Depth Buffer)2028, // 8-bit Stencil Buffer2030, // No Auxiliary Buffer204PFD_MAIN_PLANE, // Main Drawing Layer2050, // Reserved2060, 0, 0 // Layer Masks Ignored207};208209hDC = GetDC(hWnd_);210211if (!hDC) {212*error_message = "Failed to get a device context.";213return false; // Return FALSE214}215216if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) {217*error_message = "Can't find a suitable PixelFormat.";218return false;219}220221if (!SetPixelFormat(hDC, PixelFormat, &pfd)) {222*error_message = "Can't set the PixelFormat.";223return false;224}225226if (!(hRC = wglCreateContext(hDC))) {227*error_message = "Can't create a GL rendering context.";228return false;229}230231if (!wglMakeCurrent(hDC, hRC)) {232*error_message = "Can't activate the GL rendering context.";233return false;234}235236// Check for really old OpenGL drivers and refuse to run really early in some cases.237238// TODO: Also either tell the user to give up or point the user to the right websites. Here's some collected239// information about a system that will not work:240241// GL_VERSION GL_VENDOR GL_RENDERER242// "1.4.0 - Build 8.14.10.2364" "intel" intel Pineview Platform243std::string glVersion = (const char *)glGetString(GL_VERSION);244std::string glRenderer = (const char *)glGetString(GL_RENDERER);245const std::string openGL_1 = "1.";246247if (glRenderer == "GDI Generic" || glVersion.substr(0, openGL_1.size()) == openGL_1) {248//The error may come from 16-bit colour mode249//Check Colour depth250HDC dc = GetDC(NULL);251u32 colour_depth = GetDeviceCaps(dc, BITSPIXEL);252ReleaseDC(NULL, dc);253if (colour_depth != 32) {254MessageBox(0, L"Please switch your display to 32-bit colour mode", L"OpenGL Error", MB_OK);255ExitProcess(1);256}257const char *defaultError = "Insufficient OpenGL driver support detected!\n\n"258"Your GPU reports that it does not support OpenGL 2.0. Would you like to try using DirectX instead?\n\n"259"DirectX is currently compatible with less games, but on your GPU it may be the only choice.\n\n"260"Visit the forums at https://forums.ppsspp.org for more information.\n\n";261262auto err = GetI18NCategory(I18NCat::ERRORS);263std::wstring versionDetected = ConvertUTF8ToWString(glVersion + "\n\n");264std::wstring error = ConvertUTF8ToWString(err->T("InsufficientOpenGLDriver", defaultError));265std::wstring title = ConvertUTF8ToWString(err->T("OpenGLDriverError", "OpenGL driver error"));266std::wstring combined = versionDetected + error;267268bool yes = IDYES == MessageBox(hWnd_, combined.c_str(), title.c_str(), MB_ICONERROR | MB_YESNO);269270if (yes) {271// Change the config to D3D and restart.272const char *d3d9Or11 = "Direct3D 9? (Or no for Direct3D 11)";273std::wstring whichD3D9 = ConvertUTF8ToWString(err->T("D3D9or11", d3d9Or11));274bool d3d9 = IDYES == MessageBox(hWnd_, whichD3D9.c_str(), title.c_str(), MB_YESNO);275g_Config.iGPUBackend = d3d9 ? (int)GPUBackend::DIRECT3D9 : (int)GPUBackend::DIRECT3D11;276g_Config.sFailedGPUBackends.clear();277g_Config.Save("save_d3d11_fallback");278279W32Util::ExitAndRestart();280}281282// Avoid further error messages. Let's just bail, it's safe, and we can't continue.283ExitProcess(1);284}285286// Some core profile drivers elide certain extensions from GL_EXTENSIONS/etc.287// glewExperimental allows us to force GLEW to search for the pointers anyway.288glewExperimental = true;289if (GLEW_OK != glewInit()) {290*error_message = "Failed to initialize GLEW.";291return false;292}293// Unfortunately, glew will generate an invalid enum error, ignore.294glGetError();295296// Reset in case we're in a backend switch.297ResetGLExtensions();298299int contextFlags = g_Config.bGfxDebugOutput ? WGL_CONTEXT_DEBUG_BIT_ARB : 0;300301HGLRC m_hrc = nullptr;302// Alright, now for the modernity. First try a 4.4, then 4.3, context, if that fails try 3.3.303// I can't seem to find a way that lets you simply request the newest version available.304if (wglewIsSupported("WGL_ARB_create_context") == 1) {305if (simulateGLES) {306const static int simulateVersions[][2] = { {3, 2}, {3, 1}, {3, 0}, {2, 0} };307for (auto ver : simulateVersions) {308const int attribsES[] = {309WGL_CONTEXT_MAJOR_VERSION_ARB, ver[0],310WGL_CONTEXT_MINOR_VERSION_ARB, ver[1],311WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_ES2_PROFILE_BIT_EXT,3120313};314m_hrc = wglCreateContextAttribsARB(hDC, 0, attribsES);315if (m_hrc)316break;317}318}319320for (int tryCore = 1; tryCore >= 0 && m_hrc == nullptr; --tryCore) {321SetGLCoreContext(tryCore == 1);322323for (int minor = 6; minor >= 0 && m_hrc == nullptr; --minor) {324const int attribs4x[] = {325WGL_CONTEXT_MAJOR_VERSION_ARB, 4,326WGL_CONTEXT_MINOR_VERSION_ARB, minor,327WGL_CONTEXT_FLAGS_ARB, contextFlags,328WGL_CONTEXT_PROFILE_MASK_ARB, gl_extensions.IsCoreContext ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,3290330};331m_hrc = wglCreateContextAttribsARB(hDC, 0, attribs4x);332}333const int attribs33[] = {334WGL_CONTEXT_MAJOR_VERSION_ARB, 3,335WGL_CONTEXT_MINOR_VERSION_ARB, 3,336WGL_CONTEXT_FLAGS_ARB, contextFlags,337WGL_CONTEXT_PROFILE_MASK_ARB, gl_extensions.IsCoreContext ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,3380339};340if (!m_hrc)341m_hrc = wglCreateContextAttribsARB(hDC, 0, attribs33);342}343344if (!m_hrc) {345// Fall back346m_hrc = hRC;347} else {348// Switch to the new ARB context.349wglMakeCurrent(nullptr, nullptr);350wglDeleteContext(hRC);351wglMakeCurrent(hDC, m_hrc);352}353} else {354// We can't make a GL 3.x context. Use an old style context (GL 2.1 and before)355m_hrc = hRC;356}357358if (GLEW_OK != glewInit()) {359*error_message = "Failed to re-initialize GLEW.";360return false;361}362// Unfortunately, glew will generate an invalid enum error, ignore.363if (gl_extensions.IsCoreContext)364glGetError();365366if (!m_hrc) {367*error_message = "No m_hrc";368return false;369}370371hRC = m_hrc;372373bool validation = g_Config.bGfxDebugOutput;374375// Always run OpenGL validation in debug mode, just like we do with Vulkan if debug layers are installed.376#ifdef _DEBUG377validation = true;378#endif379380if (validation) {381if (wglewIsSupported("GL_KHR_debug") == 1) {382glGetError();383glDebugMessageCallback((GLDEBUGPROC)&DebugCallbackARB, nullptr);384if (glGetError()) {385ERROR_LOG(Log::G3D, "Failed to register a debug log callback");386}387glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);388if (glGetError()) {389ERROR_LOG(Log::G3D, "Failed to enable synchronous debug output");390}391} else if (glewIsSupported("GL_ARB_debug_output")) {392glGetError();393glDebugMessageCallbackARB((GLDEBUGPROCARB)&DebugCallbackARB, 0); // print debug output to stderr394if (glGetError()) {395ERROR_LOG(Log::G3D, "Failed to register a debug log callback");396}397glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);398if (glGetError()) {399ERROR_LOG(Log::G3D, "Failed to enable synchronous debug output");400}401402// For extra verbosity uncomment this (MEDIUM and HIGH are on by default):403// glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, nullptr, GL_TRUE);404}405406glEnable(GL_DEBUG_OUTPUT);407}408409pauseRequested = false;410resumeRequested = false;411412CheckGLExtensions();413draw_ = Draw::T3DCreateGLContext(wglSwapIntervalEXT != nullptr);414bool success = draw_->CreatePresets(); // if we get this far, there will always be a GLSL compiler capable of compiling these.415if (!success) {416delete draw_;417draw_ = nullptr;418ReleaseGLContext();419return false;420}421422draw_->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) {423g_OSD.Show(OSDType::MESSAGE_ERROR, details, 0.0f, "error_callback");424}, nullptr);425426// These are auto-reset events.427pauseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);428resumeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);429430renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);431renderManager_->SetInflightFrames(g_Config.iInflightFrames);432SetGPUBackend(GPUBackend::OPENGL);433renderManager_->SetSwapFunction([&]() {::SwapBuffers(hDC); });434if (wglSwapIntervalEXT) {435// glew loads wglSwapIntervalEXT if available436renderManager_->SetSwapIntervalFunction([&](int interval) {437wglSwapIntervalEXT(interval);438});439}440CHECK_GL_ERROR_IF_DEBUG();441return true; // Success442}443444void WindowsGLContext::Shutdown() {445glslang::FinalizeProcess();446}447448void WindowsGLContext::ReleaseGLContext() {449if (hRC) {450// Are we able to release the DC and RC contexts?451if (!wglMakeCurrent(NULL, NULL)) {452MessageBox(NULL, L"Release of DC and RC failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);453}454455// Are we able to delete the RC?456if (!wglDeleteContext(hRC)) {457MessageBox(NULL, L"Release rendering context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);458}459hRC = NULL;460}461462if (hDC && !ReleaseDC(hWnd_, hDC)) {463DWORD err = GetLastError();464if (err != ERROR_DC_NOT_FOUND) {465MessageBox(NULL, L"Release device context failed.", L"SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);466}467hDC = NULL;468}469hWnd_ = NULL;470}471472void WindowsGLContext::ShutdownFromRenderThread() {473delete draw_;474draw_ = nullptr;475CloseHandle(pauseEvent);476CloseHandle(resumeEvent);477ReleaseGLContext();478}479480void WindowsGLContext::Resize() {481}482483void WindowsGLContext::ThreadStart() {484renderManager_->ThreadStart(draw_);485}486487bool WindowsGLContext::ThreadFrame() {488return renderManager_->ThreadFrame();489}490491void WindowsGLContext::ThreadEnd() {492renderManager_->ThreadEnd();493}494495void WindowsGLContext::StopThread() {496renderManager_->StopThread();497}498499500