Skip to content

Faster fblits() for multiple blits of the same Surface #2825

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

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0af8c37
First implementation of caching mechanism as blitcopy optimization.
itzpr3d4t0r Apr 26, 2024
5db5c25
fixes
itzpr3d4t0r Apr 26, 2024
6ff00d6
more fixes and add missing stubs
itzpr3d4t0r Apr 26, 2024
69cbaf6
removed unused variable
itzpr3d4t0r Apr 26, 2024
7c107e4
another fix
itzpr3d4t0r Apr 26, 2024
52c4844
another fix
itzpr3d4t0r Apr 26, 2024
5508da3
remove unused variables and cast
itzpr3d4t0r Apr 26, 2024
0bc580e
moved sequence setup to _surf_fblits_cached_item_check_and_blit
itzpr3d4t0r Apr 26, 2024
0fba53e
Added SSE2 version
itzpr3d4t0r Apr 26, 2024
54dd0fa
fix
itzpr3d4t0r Apr 26, 2024
261472b
tentative fix
itzpr3d4t0r Apr 26, 2024
f2fd2e9
Massively simplified code for cached blitcopy, removed avx/sse versio…
itzpr3d4t0r Apr 27, 2024
a6ddfef
forgot about that
itzpr3d4t0r Apr 27, 2024
41b68ad
Can now partially blit surfaces onto the destination.
itzpr3d4t0r Apr 30, 2024
f24c4eb
use SDL_HasColorKey
itzpr3d4t0r May 3, 2024
701be35
remove unused variable
itzpr3d4t0r May 3, 2024
57e2aaa
cleanup, always using realloc now, added proper error messages.
itzpr3d4t0r May 3, 2024
eada109
function now respects the destination's clip rect
itzpr3d4t0r May 3, 2024
065b3f8
better support subssurfaces
itzpr3d4t0r May 7, 2024
2d2a1c0
now correctly draws onto subsurfaces
itzpr3d4t0r May 11, 2024
f91a080
removed "cache" parameter
itzpr3d4t0r May 11, 2024
f8f78a8
fix
itzpr3d4t0r May 11, 2024
b7a7d78
rename "cache" -> "multi"
itzpr3d4t0r May 12, 2024
0325472
Now properly supporting all rects/rectlike as positions, minor change…
itzpr3d4t0r May 18, 2024
5acd3a5
more changes to marginally improve performance + now using less memor…
itzpr3d4t0r May 18, 2024
7a5834b
forgot a rename
itzpr3d4t0r May 22, 2024
d50fd98
removed unused declaration
itzpr3d4t0r May 22, 2024
662fa0e
fix and rename
itzpr3d4t0r May 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions buildconfig/stubs/pygame/surface.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,14 @@ class Surface:
) -> Union[List[Rect], None]: ...
def fblits(
self,
blit_sequence: Iterable[Tuple[Surface, Union[Coordinate, RectValue]]],
special_flags: int = 0, /
blit_sequence: Iterable[
Union[
Tuple[Surface, Union[Coordinate, RectValue]],
Tuple[Surface, Sequence[Union[Coordinate, RectValue]]],
]
],
special_flags: int = 0,
/,
) -> None: ...
@overload
def convert(self, surface: Surface, /) -> Surface: ...
Expand Down
3 changes: 2 additions & 1 deletion docs/reST/ref/surface.rst
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@
.. method:: fblits

| :sl:`draw many surfaces onto the calling surface at their corresponding location and the same special_flags`
| :sg:`fblits(blit_sequence=((source, dest), ...), special_flags=0, /) -> None`
| :sg:`fblits(blit_sequence=((source, dest), ...), special_flags=0/) -> None`
| :sg:`fblits(blit_sequence=((source, [dest1, dest2, ...]), ...), special_flags=0/) -> None`

This method takes a sequence of tuples (source, dest) as input, where source is a Surface
object and dest is its destination position on this Surface. It draws each source Surface
Expand Down
79 changes: 79 additions & 0 deletions src_c/alphablit.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ blit_blend_premultiplied(SDL_BlitInfo *info);
static int
SoftBlitPyGame(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst,
SDL_Rect *dstrect, int blend_flags);

int
SoftMultiBlitPyGame(SDL_Surface *src, SDL_Surface *dst, int blend_flags,
BlitSequence *destinations);

extern int
SDL_RLESurface(SDL_Surface *surface);
extern void
Expand Down Expand Up @@ -580,6 +585,80 @@ SoftBlitPyGame(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst,
return (okay ? 0 : -1);
}

void
pg_multi_blitcopy(SDL_Surface *restrict src, SDL_Surface *restrict dst,
BlitSequence *restrict destinations)
{
Py_ssize_t i;
const int src_skip = src->pitch / 4;
const int dst_skip = dst->pitch / 4;

Uint32 *const src_start = (Uint32 *)src->pixels;

for (i = 0; i < destinations->size; i++) {
BlitDestination *item = &destinations->sequence[i];
Uint32 *dstp32 = item->pixels;
int h = item->rows;
const int copy_w = item->width * 4;
Uint32 *srcp32 = src_start + item->src_offset;

while (h--) {
memcpy(dstp32, srcp32, copy_w);
srcp32 += src_skip;
dstp32 += dst_skip;
}
}
}

int
SoftMultiBlitPyGame(SDL_Surface *src, SDL_Surface *dst, int blend_flags,
BlitSequence *destinations)
{
int okay = 1;
int src_locked = 0, dst_locked = 0;

if (SDL_MUSTLOCK(dst)) {
if (SDL_LockSurface(dst) < 0)
okay = 0;
else
dst_locked = 1;
}
if (SDL_MUSTLOCK(src)) {
if (SDL_LockSurface(src) < 0)
okay = 0;
else
src_locked = 1;
}

if (okay) {
SDL_BlendMode src_blend;
switch (blend_flags) {
case 0:
/* unhandled cases */
if (SDL_GetSurfaceBlendMode(src, &src_blend) != 0 ||
(src_blend == SDL_BLENDMODE_NONE && src->format->Amask) ||
SDL_HasColorKey(src)) {
okay = 0;
break;
}

/* blitcopy */
pg_multi_blitcopy(src, dst, destinations);
break;
default:
okay = 0;
break;
}
}

if (dst_locked)
SDL_UnlockSurface(dst);
if (src_locked)
SDL_UnlockSurface(src);

return okay ? 0 : -1;
}

/* --------------------------------------------------------- */

static void
Expand Down
2 changes: 1 addition & 1 deletion src_c/doc/surface_doc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define DOC_SURFACE "Surface((width, height), flags=0, depth=0, masks=None) -> Surface\nSurface((width, height), flags=0, Surface) -> Surface\npygame object for representing images"
#define DOC_SURFACE_BLIT "blit(source, dest, area=None, special_flags=0) -> Rect\ndraw another surface onto this one"
#define DOC_SURFACE_BLITS "blits(blit_sequence=((source, dest), ...), doreturn=True) -> [Rect, ...] or None\nblits(((source, dest, area), ...)) -> [Rect, ...]\nblits(((source, dest, area, special_flags), ...)) -> [Rect, ...]\ndraw many images onto another"
#define DOC_SURFACE_FBLITS "fblits(blit_sequence=((source, dest), ...), special_flags=0, /) -> None\ndraw many surfaces onto the calling surface at their corresponding location and the same special_flags"
#define DOC_SURFACE_FBLITS "fblits(blit_sequence=((source, dest), ...), special_flags=0/) -> None\nfblits(blit_sequence=((source, [dest1, dest2, ...]), ...), special_flags=0/) -> None\ndraw many surfaces onto the calling surface at their corresponding location and the same special_flags"
#define DOC_SURFACE_CONVERT "convert(surface, /) -> Surface\nconvert(depth, flags=0, /) -> Surface\nconvert(masks, flags=0, /) -> Surface\nconvert() -> Surface\nchange the pixel format of an image"
#define DOC_SURFACE_CONVERTALPHA "convert_alpha() -> Surface\nchange the pixel format of an image including per pixel alphas"
#define DOC_SURFACE_COPY "copy() -> Surface\ncreate a new copy of a Surface"
Expand Down
Loading
Loading