1

I wrote the simple program below:

import pygame as pg
import moderngl as mgl
import sys

class GraphicsEngine:
    def __init__(self, win_size=(1200,700)):
        pg.init()

        self.WIN_SIZE = win_size
        pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)
        pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)
        pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)

        pg.display.set_mode(self.WIN_SIZE, flags=pg.OPENGL | pg.DOUBLEBUF)

        self.ctx = mgl.create_context()

        self.clock = pg.time.Clock()

    def check_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
                pg.quit()
                sys.exit()
    
    def render(self):
        self.ctx.clear(color=(0.08, 0.16, 0.18))

        pg.display.flip()

    def run(self):
        while True:
            self.check_events()
            self.render()
            self.clock.tick(60)
            pg.display.set_caption(pg.time.get_fps())



if __name__ == '__main__':
    app=GraphicsEngine()
    app.run()

and I get this error

File "path\main.py", line 41, in <module>
    app=GraphicsEngine()
        ^^^^^^^^^^^^^^^^
  File "path\main.py", line 16, in __init__
    self.ctx = mgl.create_context()
               ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\\AppData\Roaming\Python\Python311\site->packages\moderngl\context.py", line 1968, in create_context
    ctx.mglo, ctx.version_code = >mgl.create_context(glversion=require, mode=mode, **settings)
                                 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\\AppData\Roaming\Python\Python311\site->packages\glcontext\__init__.py", line 69, in create
    return wgl.create_context(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Exception: cannot detect OpenGL context

I tried to reinstall moderngl but nothing changed

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Error
  • 9
  • 3
  • 1
    The problem is not reproducible. – Rabbid76 Jun 02 '23 at 16:49
  • Also, you say you "found a tutorial on the internet" – _which_ tutorial? If you're posting code slightly-modified from somewhere, you should link to the source. – wizzwizz4 Jun 02 '23 at 16:52
  • @wizzwizz4 No, that is not what I mean. I have tested the code and it works fine for me. So the problem is in the system and not in the code and thus the question is off-topic – Rabbid76 Jun 02 '23 at 16:53
  • @wizzwizz4 It's a problem with the system because the code works with my system. (Windows - like the contributor's system). – Rabbid76 Jun 02 '23 at 17:07
  • @Error Are you getting any `pygame`-related messages before that error? – wizzwizz4 Jun 02 '23 at 17:49
  • @Error What happens if you call `pygame.display.init()` separately? – wizzwizz4 Jun 02 '23 at 18:19

2 Answers2

3

Let's start at the end of your traceback:

  File "C:\Python311\site-packages\glcontext\__init__.py", line 69, in create
    return wgl.create_context(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Exception: cannot detect OpenGL context

Looking in __init__.py, we don't find the string "cannot detect OpenGL context". But, on line 56, we do find:

from glcontext import wgl

In moderngl's source code, we find a file wgl.cpp, with the lines (119–123):

res->hrc = res->m_wglGetCurrentContext();
if (!res->hrc) {
    PyErr_Format(PyExc_Exception, "cannot detect OpenGL context");
    return NULL;
}

I don't know what res->hrc is, but nor do I need to, because it's the return value of res->m_wglGetCurrentContext(), and that function comes from line 80:

res->m_wglGetCurrentContext = (m_wglGetCurrentContextProc)GetProcAddress(res->libgl, "wglGetCurrentContext");

A little earlier (line 73), we see that res->libgl is:

res->libgl = LoadLibraryEx(libgl, NULL, (DWORD)load_mode);

where libgl is (line 50):

const char * libgl = "opengl32.dll";

So there's a problem with the wglGetCurrentContext function from opengl32.dll. Let's check Microsoft's Win32API documentation:

If the calling thread has a current OpenGL rendering context, wglGetCurrentContext returns a handle to that rendering context. Otherwise, the return value is NULL.

The !res->hrc on line 120 is checking for NULL, so this looks like our culprit!

Now, why is there no OpenGL rendering context...?

Standalone hypothesis

The Windows API documentation tells us how to create an OpenGL context – and, indeed, there's a function res->m_wglCreateContext in wgl.cpp. It's called on line 214, inside an if block starting at line 180:

if (!strcmp(mode, "standalone")) {

Where does mode come from? Lines 49–55:

const char * mode = "detect";
const char * libgl = "opengl32.dll";
int glversion = 330;

if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ssi", keywords, &mode, &libgl, &glversion)) {
    return NULL;
}

It looks hardcoded… but it doesn't look like it's supposed to be. Let's check the Python documentation for PyArg_ParseTupleAndKeywords:

Parse the parameters of a function that takes both positional and keyword parameters into local variables. The keywords argument is a NULL-terminated array of keyword parameter names. Empty names denote positional-only parameters. Returns true on success; on failure, it returns false and raises the appropriate exception.

Not terribly useful, but Stack Overflow yields some code examples and, put together, we can see that mode is a bog-standard Python kwarg.

So, following the keyword arguments up the traceback, consulting the source code of each function, we find that changing your line 16:

self.ctx = mgl.create_context()

to:

self.ctx = mgl.create_context(standalone=True)

fixes the immediate problem: now an OpenGL context is created. However, it doesn't seem to do anything.

Pygame hypothesis

The ModernGL documentation says:

ModernGL can only create headless contexts (no window), but it can also detect and use contexts from a large range of window libraries.

As Rabbid76 identified, ModernGL is supposed to pick up the context from pygame. The fact it doesn't is curious. They should be created by pygame.init(), so let's track that down.

>>> import pygame
pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
>>> pygame.init
<built-in function init>
>>> pygame.init.__doc__
'init() -> (numpass, numfail)\ninitialize all imported pygame modules

So pygame.init is defined in C – as one might expect. base.c's pg_init has an identical function and an identical return type, so I'll assume that's it. Lines 341–343:

/* Put all the module names we want to init in this array */
const char *modnames[] = {
    IMPPREFIX "display", /* Display first, this also inits event,time */
    IMPPREFIX "joystick", IMPPREFIX "font", IMPPREFIX "freetype",
    IMPPREFIX "mixer",
    /* IMPPREFIX "_sdl2.controller", Is this required? Comment for now*/
    NULL};

Lines 361–368:

if (pg_mod_autoinit(modnames[i]))
    success++;
else {
    /* ImportError is neither counted as success nor failure */
    if (!PyErr_ExceptionMatches(PyExc_ImportError))
        fail++;
    PyErr_Clear();
}

pgmod_autoinit looks for a function named _internal_mod_init and, failing that, a function called init. The pygame.display module handles SDL2, which handles OpenGL contexts; display._internal_mod_init doesn't exist, but pygame.display.init does. The relevant section:

const char *drivername;
/* Compatibility:
 * windib video driver was renamed in SDL2, and we don't want it to fail.
 */
drivername = SDL_getenv("SDL_VIDEODRIVER");
if (drivername &&
    !SDL_strncasecmp("windib", drivername, SDL_strlen(drivername))) {
    SDL_setenv("SDL_VIDEODRIVER", "windows", 1);
}
if (!SDL_WasInit(SDL_INIT_VIDEO)) {
    if (!_pg_mac_display_init())
        return NULL;

    if (SDL_InitSubSystem(SDL_INIT_VIDEO))
        return RAISE(pgExc_SDLError, SDL_GetError());
}

(return NULL; means an exception occurred, and it should be re-raised.)

SDL2's code is pretty solid, so there's probably no bug there. However, recall the code from base.c: if this code fails, pygame.init() will swallow the exception, and the only way you'd know anything went wrong is if you checked the return value.

It's probably possible to debug this further by looking into SDL2's source code, to find the conditions under which wglCreateContext doesn't get called, but the easiest way would be to check pygame.init()'s return value.

wizzwizz4
  • 6,140
  • 2
  • 26
  • 62
  • @Rabbid76 That'll teach me to be lazy. Fixed. – wizzwizz4 Jun 02 '23 at 17:40
  • `self.ctx = mgl.create_context(standalone=True)` causes my system to hang. `self.ctx = mgl.create_context()` still works fine. – Rabbid76 Jun 02 '23 at 17:42
  • @Rabbid76 Okay, [the documentation shed some light into this issue](https://moderngl.readthedocs.io/en/latest/the_guide/basic.html). Maybe we should've started with that. – wizzwizz4 Jun 02 '23 at 17:44
  • `ctx = moderngl.create_context()` detects an active context. The context is created by Pygame. Also see https://moderngl.readthedocs.io/en/latest/reference/context.html – Rabbid76 Jun 02 '23 at 17:45
-1

I found a solution to my problem. it was some problem with environment variables, it was enough to remove one variable that shouldn't be there and reinstall moderngl. thanks for trying to help.

Error
  • 9
  • 3
  • 1
    Which "variable" did you remove? – Mehdi Charife Jun 03 '23 at 10:46
  • I dont remember full name but it's like OPENGL_CONTEXT_(propably)CREATE – Error Jun 03 '23 at 12:51
  • It was only not windows user variable(all variables I adding for all user) – Error Jun 03 '23 at 13:06
  • @Error, thanks for posting what worked (though it would've been nice to have more details). Future readers, if you've encountered this issue, please record the name of the environment variable! (And re-installing moderngl _probably_ has nothing to do with fixing this issue.) – wizzwizz4 Jun 03 '23 at 14:53