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

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



Hi again,

On 17-04-17 07:59 AM, Ian Mallett wrote:
On Mon, Apr 17, 2017 at 12:01 AM, Lenard Lindstrom <len-l@xxxxxxxxx <mailto:len-l@xxxxxxxxx>> wrote:

        ​Is it possible to combine the renderer with the window? I
        don't see why the renderer needs to be pulled out of the
        pygame.draw module, and indeed, this could be confusing for
        existing code.

    I don't quite understand. The renderer associated with the
    RendererWindow instance is returned by the window's renderer property.

    Why are the window and the renderer separate objects? There is a
    one-to-one relation between a window and its renderer or a display
    surface. And a window cannot have both. To avoid duplicating a
    bunch of window specific methods I created a Window class, with
    RendererWindow and SurfaceWindow (not shown) as subclasses. Also,
    a surface can have a renderer, and a combined surface-renderer
    object makes no sense. Still, I haven't ruled out renderer-window
    and surface-window types yet. However it is done, I don't see an
    elegant representation of the relationships among SDL windows,
    surfaces, renders, and textures.


​I guess I'm asking why they have to be separate objects. At the OS level, there's a region of the screen which is backed either by a software buffer and managed by the compositor or a GPU buffer managed by the compositor via the graphics context. It's a buffer either way, and the difference is sortof only where it lives and what operations are fast on it.

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.

So, it's not really clear to me what a "renderer" is (I guess it's supposed to wrap the graphics context?).
According to the SDL Wiki, a renderer is for "2D accelerated rendering"[1]. It "[s]upports easy rotation, scaling and alpha blending, all accelerated using modern 3D APIs".[2] I guess it uses the GPU to manipulate textures loaded into video memory. At least that is what the C api suggests. And I don't see how it can be made to resemble surface blits.


Separately, I was thinking also that anything that does "rendering" ought to be combined with pygame.draw, because principle of least astonishment. However, this sortof depends on the new drawing API, and whether it can/should remain backward compatible with pygame.draw.
A SDL 2 renderer has little to do with pygame.draw. A renderer is a collection of textures and some functions to draw them to a window.


        For maximum backcompatibility, something like the following
        would seem to fit better with the existing API:

        surf1 = pygame.display.Window(rect1,flags)
        surf2 = pygame.display.Window(rect2,flags)
        #...
        surf1.blit(...)
        surf2.blit(...)
        #...
        pygame.display.flip()

        I don't recall what was decided about the feasibility of
        implementing SDL2-style or hardware-accelerated rendering, but
        I'd hazard that this sort of API wouldn't map well to it.
        OTOH, I don't think the decision to implement a modern
        graphics interface was decided in the first place (just that
        we're currently adding some SDL2 stuff).

    The user can choose either a renderer or a display surface for a
    window. And pixels can be copied between textures and surfaces. So
    hardware accelerated rendering is available, as shown in the example.

    As for set_mode(), flip(), and update(), they remain in Pygame SDL
    2. Keeping as much of the Pygame SDL 1 API in Pygame SDL 2 is a
    priority. This example explores how new SDL 2 specific
    capabilities can be added to Pygame. It uses a Python feature
    unavailable when Pygame was created, the new-style class.


​My point was more that the proposed API substantially changes the way programming in pygame feels. I don't know; maybe this is necessary.​
It will if using a renderer and textures or opening multiple windows. But the old display surface api will remain. Also, Pygame's Python API is old. A class based approach adds more flexibility.


        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.


2: Discourage this pathological situation in the pygame API "somehow".

Multiple windows is a feature of SDL 2. But it also works with just one, or none.

[1] https://wiki.libsdl.org/CategoryRender

[2] https://wiki.libsdl.org/Introduction


Lenard Lindstrom