[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Pygame for SDL 2, and how it may look.



On Mon, Apr 17, 2017 at 4:28 PM, Lenard Lindstrom <len-l@xxxxxxxxx> wrote:
I agree that a window and its renderer or a window and its display surface belong together as a single object. And I am looking at doing that now. It is just I am now running into the limitations of Python and Cython. Extension types only support single inheritance. To add full window support—title, hiding, resizing, brightness, and more—to both a Surface subclass and a Renderer subclass means duplicating all that window specific code for both subclass declarations. If Cython supported mixins (and I thought it could, but no such luck), it would be easy. It just seemed safer avoid code duplication with a Window class instead.

​Unfortunately, I know next to nothing about interfacing Python with C, so I cannot suggest anything useful.​

        It's worth noting that switching the graphics context between
        windows (to do hardware draws) isn't free, and simple code
        (that e.g. draws a bunch of things on two windows, switching
        every draw call for code clarity) might not run very well.
        Perhaps the API should discourage this somehow, especially if
        full HW drawing is encouraged.


    Again I don't understand. Given a window, its renderer, and a
    bunch of textures: render the textures to various locations on the
    window, update (expose) the renderer, then repeat. How is this not
    hardware drawing. What am I missing?


​Mmm I realize I was not very clear. Let me try again: whenever you /transition/ from HW drawing in one window to HW drawing into another, the windowing system (SDL, SDL2, etc.) needs to internally call something like:

​​
wglMakeCurrent(window,context); //(on Windows)
glXMakeCurrent(display,window,context); //(on X11)
//Other platforms similar

This call does a bunch of bureaucratic stuff, mainly to rebind the GPU's destination framebuffer onto the new window's HW surface. To the point, that call is actually fairly expensive, and if you did something in a revamped pygame like:

for i in range(100):
window1.draw(lines[i])
window2.draw(lines[i])

Then the windowing system needs to insert 199 calls to it.

There are two options that occur to me:

1: Set up a graphics context that will be shared by all windows created. Draw calls go to a per-window hardware buffer. Then display.flip() iterates through all extant windows, binds to the window, and draws the hardware buffer into it. This adds the overhead of a second pass, but would perform okay. (It is /critical/ that the graphics context be shared, or else draws will incur the cost of binds also and we're back to square 0. It's not hard to make graphics contexts shared, but IDK if/what SDL2 does.)
I don't see anything in SDL 2 for binding contexts. Maybe SDL does that for you. I don't know. If there is a way to do this in the SDL api, it will get exposed in the Pygame API at some point. The OpenGL stuff will also be added at some point.

I think you misunderstand. This isn't a feature to be exposed; it's a syscall fundamental to how windowing systems work. SDL (or any other HW-drawing-supporting library) categorically must do this, because this is simply how the GPU writes to the screen.

In SDL 1.2.15, you'll find "wglMakeCurrent(...)" in "src/video/wincommon/SDL_wingl.c" and in SDL 2.0.5, in "src/video/windows/SDL_windowsopengl.c". I don't offhand know the equivalents for Direct3D / Vulkan / GLES / Metal / Mantle / etc., but I guarantee that they exist and are called, iff interfacing to the GPU through that API on that platform is supported. As the documentation for e.g. "SDL_CreateRenderer(...)" hints, a renderer is (apparently) a hardware graphics context (possibly) tied to a target window.

When you use a windowing system, you shouldn't need to call this yourself, because the whole point of a windowing system is that it handles adjudicating the OS's compositor against the GPU for you. However, I bring it up because certain use cases will lose performance because of it, if one is unaware this is happening behind your back.

It boils down to figuring out (or testing) what SDL does. Something like seeing whether this:
for i in range(100):
    window1.hw_draw(lines[i])
    window2.hw_draw(lines[i])
...is as fast as this:
for i in range(100):
    window1.hw_draw(lines[i])
for i in range(100):
    window2.hw_draw(lines[i])
If it is, no problem. If it isn't, then the above is why.

The TL;DR of this whole discussion, I guess, is simply:

Repeatedly switching the target window for draw calls during the frame is slow. However, if SDL's implementation anticipates this (and someone should check that), or if such can be configured during SDL setup, it will be less bad. Maybe we can sidestep investigating by exposing a different API.

Ian