-
-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port Renderer to C code #3327
base: main
Are you sure you want to change the base?
Port Renderer to C code #3327
Conversation
commit 813f384 Author: Josip Komljenović <[email protected]> Date: Wed Jan 29 15:43:45 2025 +0100 Add interface for _sdl2.video classes commit 702958c Author: Josip Komljenović <[email protected]> Date: Wed Jan 29 15:38:01 2025 +0100 Add interface for _sdl2.video classes commit 081b032 Author: Josip Komljenović <[email protected]> Date: Wed Jan 29 15:33:20 2025 +0100 Add interface for _sdl2.video classes commit c9ffd1c Author: Josip Komljenović <[email protected]> Date: Wed Jan 29 15:25:10 2025 +0100 Add interface for _sdl2.video classes
9710ba9
to
79bc099
Compare
Pulled the relevant bit of logs for the WASM build failure:
|
Fixed |
src_c/render.c
Outdated
else if (PyObject_HasAttrString(sourceobj, "draw")) { | ||
PyObject *draw_method = PyObject_GetAttrString(sourceobj, "draw"); | ||
if (draw_method && PyCallable_Check(draw_method)) { | ||
PyObject_CallMethodObjArgs(sourceobj, PyUnicode_FromString("draw"), | ||
areaobj, destobj, NULL); | ||
Py_DECREF(draw_method); | ||
} | ||
else { | ||
return RAISE(PyExc_AttributeError, "source.draw is not callable"); | ||
} | ||
} | ||
else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this code could be simpler.
It checks a property, grabs the function, checks if its a callable, then calls it by looking it up again. -- Wouldn't PyUnicode_FromString("draw") leak?
If you're going to check that it's callable first you should use https://docs.python.org/3/c-api/call.html#c.PyObject_CallFunctionObjArgs because you've already looked it up you don't need to look it up again.
But I'm sorta thinking instead of fetching it, checking it, and then calling it you could just try to call it? Let Python do those checks for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I changed it, and when testing it looks kinda ok. Can you check if that is what you meant.
if (self->target == NULL) { | ||
Py_RETURN_NONE; | ||
} | ||
Py_INCREF(self->target); | ||
return (PyObject *)self->target; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you stored the None object instead of NULL you could have a unified codepath here. Not sure if that makes it harder other places though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea I see what you meant, and here indeed it would be better. Though the target
in pgRendererObject
is defined as pgTextureObject
, and I think it is better than PyObject
that could then accept Py_None. I would go with the current version, though if you want I can change it to PyObject
return RAISE(PyExc_TypeError, "surface must be None or a Surface"); | ||
} | ||
surface = (pgSurfaceObject *)surfobj; | ||
Py_INCREF(surface); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm suspicious of this incref. We're not taking ownership of the surface, we just are using it randomly in this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is needed because in line 410 we return that surface. In the else branch (without passing surface as the argument to that function) it works ok, because I guess pgSurface_New() increases the ref, so I put it only in that if branch. Without it, the following code
surface = pygame.Surface((100, 100))
window = pygame.Window(size=(100, 100))
renderer = _render.Renderer(window)
renderer.to_surface(surface)
print(surface)
segfaults on print(surface)
line (or basically any call of the surface)
src_c/render.c
Outdated
&p3, &p4)) { | ||
return NULL; | ||
} | ||
if (!pg_TwoFloatsFromObj(p1, &vertices[0].position.x, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope this doesn't sound nitpicky. In this draw and fill methods depending on the amount of points there is a great repetition of the pg_TwoFloatsFromObj code with an increasing vertex index and later the repetition of vertices[index].color = color. I think both of them could be put into C loops for all of the methods. If you don't want to make a temporary array with p1, p2, p3... you can create a temporary macro before this methods and undefine it after, since that code pattern is used a lot around here. You can also ignore all of this, it's only about my biased view on code quality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ofc, it is not nitpicky, I am always happy for good review. Yea I agree with you, I changed it a bit, can you check if it is something you also thought or you would like some extra changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔽
This PR converts pygame._sdl2.video.Renderer to C code. It has the implementation of all the Renderer methods and attributes. It doesn't have docs/type hints/tests or stuff like that.