简体   繁体   中英

How to map video memory from python to c?

I have been fooling around with some simple graphics things in Python, but to improve performance, I'd like to do some stuff in C.

I know how to transfer arrays and stuff back and forth. That's no biggie. But I thought it might be beneficial if I could just create a windows with a canvas, pass a pointer to the video memory (of course not the physical video memory) and then let Python take care of just putting that memory to the screen, while the rest is done in C. Possibly asynchronously, but I don̈́'t know if that matters.

Is this possible, and does it make sense? Or am I on the completely wrong track?

My efforts so far:

# graphics.py

import ctypes
import pygame

screen = pygame.display.set_mode((300,200))

f = ctypes.CDLL('/path/to/engine.so')

f.loop(screen._pixels_address)

And

// engine.c

#include <stdint.h>

void loop(void *mem) {
    while(1) {
        uint8_t *p = (uint8_t*) mem;
      
        // Was hoping this would make some pixels change
        for(int i=0; i<20000; i++)
            *p=127;
    }
}

This didn't work and ended up with a crash. I'm not surprised, but well, it's what I've got so far.

It's not strictly necessary to use Python. But I do want to use C. And I'm also aware that in most cases, my approach is not the best one, but I do enjoy coding in C and doing stuff old school.

Essentially, you want to share a buffer with the Python application. There is a buffer protocol which exists for this exact purpose. Essentially, you share a Py_buffer object which has a buf and len field. (I didn't read through the whole thing, but you can for more info.)

It turns out, that Surface.get_buffer() already returns such a buffer object. In other words, you can directly pass it to your external function:

import pygame

background_colour = (255,255,255) # For the background color of your window
(width, height) = (300, 200) # Dimension of the window

screen = pygame.display.set_mode((width, height))

import engine # engine.so

b_running = True
while b_running:
    # Quit when 'X' button is pressed.
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            b_running = False

    # Add some amount of delay.
    pygame.time.wait(50)

    # Give the buffer object to C.
    engine.paint_gray(screen.get_buffer())
    pygame.display.flip()

The key here is:

engine.paint_gray(screen.get_buffer())

This isn't exactly what you were doing, but I think you should keep the loop in the Python application to be able to handle events. Therefore, this function simply paints on the buffer. This is how this is implemented in C:

// Related: https://docs.python.org/3/extending/extending.html

#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include <stdint.h>
#include <assert.h>

static PyObject* engine_paint_gray(PyObject *self, PyObject *args)
{
    Py_buffer buffer;

    if (!PyArg_ParseTuple(args, "s*", &buffer))
        assert(0);

    uint8_t *raw_buffer = buffer.buf;
    for (int index=0; index<buffer.len; ++index)
        raw_buffer[index] = 127;

    return Py_None;
}

static PyMethodDef EngineMethods[] = {
    {"paint_gray", engine_paint_gray, METH_VARARGS, NULL},
    {NULL, NULL, 0, NULL} /* sentinel */
};

static struct PyModuleDef enginemodule = {
    PyModuleDef_HEAD_INIT,
    "engine",
    NULL,
    -1,
    EngineMethods
};

PyMODINIT_FUNC
PyInit_engine(void)
{
    return PyModule_Create(&enginemodule);
}

Note that I am also calling pygame.display.flip() . This is necessary, because pygame keeps two buffers one front buffer that is drawn on the screen and one back buffer that can be modified.

Most GUI toolkits and game libraries will provide one way or another of getting access to the pixel buffer.

An easy way to get started would be with pygame. The get_buffer().raw array of pygame's Surface can be passed into a C program.

import pygame

background_colour = (255,255,255) # For the background color of your window
(width, height) = (300, 200) # Dimension of the window

screen = pygame.display.set_mode((width, height))

f = CDLL('engine.so')
f.loop(screen.get_buffer().raw)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM