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

Re: blits proposal, was Re: [pygame] Dirty rect overlapping optimizations



Hellos,

I made an initial implementation of Surface.blits(), focusing just on correctness, with no optimizations.
https://github.com/pygame/pygame/pull/439

In the micro benchmark below it takes from 88% to 92% of the time for 255 surfaces
compared to using Surface.blit in python in a loop over the same list.


   .. method:: blits

      | :sl:`draw many images onto another`
      | :sg:`blits(blit_sequence=(source, dest), ...), doreturn=1) -> (Rect, ...)`
      | :sg:`blits((source, dest, area), ...)) -> (Rect, ...)`
      | :sg:`blits((source, dest, area, special_flags), ...)) -> (Rect, ...)`

      Draws many surfaces onto this Surface. It takes a sequence as input,
      with each of the elements corresponding to the ones of ``Surface.blit()``.
      It needs at minimum a sequence of (source, dest).



Considering blit is usually the slow part in most pygame apps, this is sort of nice.

Note, I haven't updated pygame.sprite to use it. Volunteers?
I feel without updating pygame.sprite, many people won't use it.


Some benchmarking and other notes below.


cheers,




1) Why not work on a faster implementation that saves the unwrapped objects?
This would allow you to save a list into a C object like:

struct blitinfo {
 SDL_Surface dest;
 GAME_Rect *src_rect;
 GAME_Rect *area;
 int flags;
}

Then if you promise not to change the C list (ie, you are updating rects in place, and all your Surfaces are still there),
then it could avoid a lot of the unwrapping work.

However, I did a test where I commented out the blit call. So only the unwrapping and looping over the list is done.
And it seems that the python book keeping for these 255 10x10 surfaces is only 2.1%-3.3% of the total time taken.



2) Another optimization would be to avoid subsurface checks, and avoid a few other preparations for surfaces.
I tried this, and didn't see any noticeable improvement.

3) Currently neither SDL1 or SDL2 have a special batched blit, but there are proposals and implementations around.
Such as SDL_GPU.
This could see a bigger improvement on such backends where changing state is slow (OpenGL etc).





import pygame
from pygame.locals import *
NUM_SURFS = 255
dst = pygame.Surface((NUM_SURFS * 10, 10), SRCALPHA, 32)
dst.fill((230, 230, 230))

blit_list = []
for i in range(NUM_SURFS):
    dest = (i * 10, 0)
    surf = pygame.Surface((10, 10), SRCALPHA, 32)
    color = (i * 1, i * 1, i * 1)
    surf.fill(color)
    blit_list.append((surf, dest))

def blits(blit_list):
    for surface, dest in blit_list:
        dst.blit(surface, dest)



In [17]: %timeit results = blits(blit_list)
774 µs ± 24.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [18]: %timeit results = dst.blits(blit_list)
717 µs ± 12.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [19]: %timeit results = dst.blits(blit_list, doreturn=0)
688 µs ± 14.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [20]: (100. / 774) * 717
Out[20]: 92.63565891472868

In [21]: (100. / 774) * 688
Out[21]: 88.88888888888889



If I comment out the actual blit call...

In [3]:  %timeit results = dst.blits(blit_list)
26.2 µs ± 695 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [4]: %timeit results = dst.blits(blit_list, doreturn=0)
17.6 µs ± 314 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


In [5]: (100. / 774) * 26
Out[5]: 3.3591731266149867

In [6]: (100. / 774) * 17
Out[6]: 2.1963824289405682




On Thu, Mar 30, 2017 at 7:12 PM, Leif Theden <leif.theden@xxxxxxxxx> wrote:
Pull request sent with basic functionality of Surface.blits.

https://bitbucket.org/pygame/pygame/pull-requests/79

On Thu, Mar 30, 2017 at 2:44 AM, René Dudfield <renesd@xxxxxxxxx> wrote:
Not sure. I need to do some benchmarking, and then more research into if the API can cover everything we want it to do. (which of course means figuring it out). So far I think the API I proposed will help with the Groups we have, and will also help with people who don't care about dirty rects.

It's not really much code... as you can see Leif has already done something like it... it's just a for loop around blit basically.
If someone who just knows python wants to help, they could begin writing unit tests for it in test/surface_test.py

My pygame things are basically these at the moment. If anyone wants to do the blits() API, that's fine too!

* keep improving https://pygame.org/wiki/GettingStarted , gather and file usability bugs with pip, python, pygame...
* help finalise github move decision/move.
* blits
* cython sprites.*Group
* SDL2 stuff


cheers,




On Thu, Mar 30, 2017 at 8:02 AM, DiliupG <diliupg@xxxxxxxxx> wrote:
When will this fabulous upgrade be available? :)

On 29 March 2017 at 12:58, René Dudfield <renesd@xxxxxxxxx> wrote:
On Wed, Mar 29, 2017 at 8:33 AM, Radomir Dopieralski <pygame@xxxxxxxxxxxx> wrote:
I think that special_flags could be shared for all the blits in the
list. I can't think of a case where I would want to use different flags
for each.


Yeah, I think you may be right. When I've used it (for particles as an example) it was for everything in a Group. I searched through some code online and found a few uses where it was used in Sprites. But then all of those Sprites were rendered the same. There's some sort of nice symmetry by using the same arguments with blit, and blits however.



--
http://www.diliupg.com

**********************************************************************************************
This e-mail is confidential. It may also be legally privileged. If you are not the intended recipient or have received it in error, please delete it and all copies from your system and notify the sender immediately by return e-mail. Any unauthorized reading, reproducing, printing or further dissemination of this e-mail or its contents is strictly prohibited and may be unlawful. Internet communications cannot be guaranteed to be timely, secure, error or virus-free. The sender does not accept liability for any errors or omissions.
**********************************************************************************************