From 5cb1096c2186dd880539acf3ad6f95497d18a770 Mon Sep 17 00:00:00 2001 From: Starbuck5 <46412508+Starbuck5@users.noreply.github.com> Date: Tue, 5 Nov 2024 01:20:34 -0800 Subject: [PATCH] Another pg_Int/Float helper optimization approach. --- src_c/base.c | 109 +++++++++++++++++++++++++++++++++++----------- src_c/rect_impl.h | 1 + src_c/surface.c | 76 +++++++++----------------------- 3 files changed, 105 insertions(+), 81 deletions(-) diff --git a/src_c/base.c b/src_c/base.c index 2761c8c3aa..c04b7905f9 100644 --- a/src_c/base.c +++ b/src_c/base.c @@ -496,24 +496,19 @@ pg_base_get_init(PyObject *self, PyObject *_null) static int pg_IntFromObj(PyObject *obj, int *val) { - int tmp_val; - if (PyFloat_Check(obj)) { /* Python3.8 complains with deprecation warnings if we pass * floats to PyLong_AsLong. */ - double dv = PyFloat_AsDouble(obj); - tmp_val = (int)dv; + *val = (int)PyFloat_AS_DOUBLE(obj); } else { - tmp_val = PyLong_AsLong(obj); - } - - if (tmp_val == -1 && PyErr_Occurred()) { - PyErr_Clear(); - return 0; + *val = PyLong_AsLong(obj); + if (*val == -1 && PyErr_Occurred()) { + PyErr_Clear(); + return 0; + } } - *val = tmp_val; return 1; } @@ -535,17 +530,28 @@ pg_IntFromObjIndex(PyObject *obj, int _index, int *val) static int pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2) { - if (PyTuple_Check(obj) && PyTuple_Size(obj) == 1) { + // First, lets check the size. This returns -1 if invalid and may set an error. + Py_ssize_t obj_size = PySequence_Size(obj); + + // If the object is a tuple of one element, try that one element. + if (obj_size == 1 && PyTuple_Check(obj)) { return pg_TwoIntsFromObj(PyTuple_GET_ITEM(obj, 0), val1, val2); } - if (!PySequence_Check(obj) || PySequence_Length(obj) != 2) { + + // Otherwise lets make sure this is a legit sequence and has two elements. + // Some objects can passing PySequence_Size but fail PySequence_Check + // (like sets) + if (obj_size != 2 || !PySequence_Check(obj)) { + PyErr_Clear(); // Clear the potential error from PySequence_Size return 0; } - // Can use PySequence_ITEM because of the PySequence_Check above. + // Now we can extract the items, using this macro because we know + // obj is a PySequence. PyObject *item1 = PySequence_ITEM(obj, 0); PyObject *item2 = PySequence_ITEM(obj, 1); + // If either item is NULL lets get out of here if (item1 == NULL || item2 == NULL) { Py_XDECREF(item1); Py_XDECREF(item2); @@ -553,7 +559,26 @@ pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2) return 0; } - if (!pg_IntFromObj(item1, val1) || !pg_IntFromObj(item2, val2)) { + // Fastest way to extract numbers I tested (in Python 3.13) is to extract + // Python floats as doubles with the below macro, and get everything else + // through PyLong_AsLong, using C casting to turn into the final type. + if (PyFloat_Check(item1)) { + *val1 = (int)PyFloat_AS_DOUBLE(item1); + } + else { + *val1 = PyLong_AsLong(item1); + } + + if (PyFloat_Check(item2)) { + *val2 = (int)PyFloat_AS_DOUBLE(item2); + } + else { + *val2 = PyLong_AsLong(item2); + } + + // This catches the case where either of the PyLong_AsLong's failed + if ((*val1 == -1 || *val2 == -1) && PyErr_Occurred()) { + PyErr_Clear(); Py_DECREF(item1); Py_DECREF(item2); return 0; @@ -567,14 +592,16 @@ pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2) static int pg_FloatFromObj(PyObject *obj, float *val) { - float f = (float)PyFloat_AsDouble(obj); - - if (f == -1 && PyErr_Occurred()) { - PyErr_Clear(); - return 0; + if (PyFloat_Check(obj)) { + *val = (float)PyFloat_AS_DOUBLE(obj); + } + else { + *val = (float)PyLong_AsLong(obj); + if (*val == -1.0f && PyErr_Occurred()) { + PyErr_Clear(); + return 0; + } } - - *val = f; return 1; } @@ -596,17 +623,28 @@ pg_FloatFromObjIndex(PyObject *obj, int _index, float *val) static int pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2) { - if (PyTuple_Check(obj) && PyTuple_Size(obj) == 1) { + // First, lets check the size. This returns -1 if invalid and may set an error. + Py_ssize_t obj_size = PySequence_Size(obj); + + // If the object is a tuple of one element, try that one element. + if (obj_size == 1 && PyTuple_Check(obj)) { return pg_TwoFloatsFromObj(PyTuple_GET_ITEM(obj, 0), val1, val2); } - if (!PySequence_Check(obj) || PySequence_Length(obj) != 2) { + + // Otherwise lets make sure this is a legit sequence and has two elements. + // Some objects can passing PySequence_Size but fail PySequence_Check + // (like sets) + if (obj_size != 2 || !PySequence_Check(obj)) { + PyErr_Clear(); // Clear the potential error from PySequence_Size return 0; } - // Can use PySequence_ITEM because of the PySequence_Check above. + // Now we can extract the items, using this macro because we know + // obj is a PySequence. PyObject *item1 = PySequence_ITEM(obj, 0); PyObject *item2 = PySequence_ITEM(obj, 1); + // If either item is NULL lets get out of here if (item1 == NULL || item2 == NULL) { Py_XDECREF(item1); Py_XDECREF(item2); @@ -614,7 +652,26 @@ pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2) return 0; } - if (!pg_FloatFromObj(item1, val1) || !pg_FloatFromObj(item2, val2)) { + // Fastest way to extract numbers I tested (in Python 3.13) is to extract + // Python floats as doubles with the below macro, and get everything else + // through PyLong_AsLong, using C casting to turn into the final type. + if (PyFloat_Check(item1)) { + *val1 = (float)PyFloat_AS_DOUBLE(item1); + } + else { + *val1 = (float)PyLong_AsLong(item1); + } + + if (PyFloat_Check(item2)) { + *val2 = (float)PyFloat_AS_DOUBLE(item2); + } + else { + *val2 = (float)PyLong_AsLong(item2); + } + + // This catches the case where either of the PyLong_AsLong's failed + if ((*val1 == -1.0f || *val2 == -1.0f) && PyErr_Occurred()) { + PyErr_Clear(); Py_DECREF(item1); Py_DECREF(item2); return 0; diff --git a/src_c/rect_impl.h b/src_c/rect_impl.h index 3f4084ff9c..1aae733ce3 100644 --- a/src_c/rect_impl.h +++ b/src_c/rect_impl.h @@ -384,6 +384,7 @@ #endif // RectOptional_FREELIST // #endregion RectOptional +// This is the thing that is whack #define PrimitiveType RectImport_primitiveType #define RectObject RectImport_RectObject #define OtherRectObject RectImport_OtherRectObject diff --git a/src_c/surface.c b/src_c/surface.c index d4a8eae2e9..8add0e2fb3 100644 --- a/src_c/surface.c +++ b/src_c/surface.c @@ -488,6 +488,7 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds) &depth, &masks)) return -1; + // REPLACE with pg_TwoIntsFromObj if (PySequence_Check(size) && PySequence_Length(size) == 2) { if ((!pg_IntFromObjIndex(size, 0, &width)) || (!pg_IntFromObjIndex(size, 1, &height))) { @@ -711,63 +712,28 @@ surface_init(pgSurfaceObject *self, PyObject *args, PyObject *kwds) static PyObject * surf_get_at(PyObject *self, PyObject *position) { - SDL_Surface *surf = pgSurface_AsSurface(self); - SDL_PixelFormat *format = NULL; - Uint8 *pixels = NULL; - int x, y; - Uint32 color; - Uint8 *pix; - Uint8 rgba[4] = {0, 0, 0, 255}; - - SURF_INIT_CHECK(surf) - - if (!pg_TwoIntsFromObj(position, &x, &y)) { - return RAISE(PyExc_TypeError, - "position must be a sequence of two numbers"); - } - - if (x < 0 || x >= surf->w || y < 0 || y >= surf->h) - return RAISE(PyExc_IndexError, "pixel index out of range"); - - format = surf->format; - - if (PG_FORMAT_BytesPerPixel(format) < 1 || - PG_FORMAT_BytesPerPixel(format) > 4) - return RAISE(PyExc_RuntimeError, "invalid color depth for surface"); - - if (!pgSurface_Lock((pgSurfaceObject *)self)) - return NULL; - - pixels = (Uint8 *)surf->pixels; - - switch (PG_FORMAT_BytesPerPixel(format)) { - case 1: - color = (Uint32) * ((Uint8 *)pixels + y * surf->pitch + x); - SDL_GetRGB(color, format, rgba, rgba + 1, rgba + 2); - break; - case 2: - color = (Uint32) * ((Uint16 *)(pixels + y * surf->pitch) + x); - SDL_GetRGBA(color, format, rgba, rgba + 1, rgba + 2, rgba + 3); - break; - case 3: - pix = ((Uint8 *)(pixels + y * surf->pitch) + x * 3); -#if SDL_BYTEORDER == SDL_LIL_ENDIAN - color = (pix[0]) + (pix[1] << 8) + (pix[2] << 16); -#else - color = (pix[2]) + (pix[1] << 8) + (pix[0] << 16); -#endif - SDL_GetRGB(color, format, rgba, rgba + 1, rgba + 2); - break; - default: /* case 4: */ - assert(PG_FORMAT_BytesPerPixel(format) == 4); - color = *((Uint32 *)(pixels + y * surf->pitch) + x); - SDL_GetRGBA(color, format, rgba, rgba + 1, rgba + 2, rgba + 3); - break; + //int x;// y; + float fx, fy; + //float fx; + //double dx, dy; + + // idea: do some more exact checks first and then go into the unexact + // checks. + + // PyObject_Print(position, stdout, 0); + + for (int i = 0; i < 10000000; i++) { + //if (!pg_IntFromObj(position, &x)) { + //if (!pg_FloatFromObj(position, &fx)) { + //if (!pg_TwoIntsFromObj(position, &x, &y)) { + if (!pg_TwoFloatsFromObj(position, &fx, &fy)) { + // if (!pg_TwoDoublesFromObj(position, &dx, &dy)) { + return RAISE(PyExc_TypeError, + "position must be a sequence of two numbers"); + } } - if (!pgSurface_Unlock((pgSurfaceObject *)self)) - return NULL; - return pgColor_New(rgba); + Py_RETURN_NONE; } static PyObject *