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

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

My votes:
  • don't bother with return value.  If a person cared (probably doesn't), then the rects can be inferred from the list sent to Surface.blits()  (again, not a fan of dirty rects)
  • or, make the default behavior not return a list of rects (return=False).  default-on would be incorrect, imo.
  • many sources to many destinations would be complex, and there are no other pygame methods that operate like that (that i am aware of). so no for now, unless very easy to implement (probably not).

I would maybe consider enforcing a fixed tuple size that is sent to the function.  The cost of dynamically unpacking and checking the length of each might be detrimental (benchmark, maybe?)

I'm not so sure that multiprocessing is a way forward, because of the lock contention involved with the destination surface.  Complex schemes to make lists of non-overlapping rects and delegating to workers smells np-complete to me.  Threads/processes waiting around for a lock seems like it would ruin the benefits of parallelism.  Who knows, however?

This would be a great benefit for the built in sprite groups.  Looking forward to seeing it implemented.

On Tue, Mar 28, 2017 at 11:35 PM, René Dudfield <renesd@xxxxxxxxx> wrote:

this blit_list, blits, blit_mult, blit_many call...

I feel we should call it "Surface.blits". To go with drawline/drawlines.
It would take a sequence of tuples which match up with the blit arguments.

The original Surface.blit API.
  blit(source, dest, area=None, special_flags = 0) -> Rect

The new blits API.
    blits(args) -> [rects]
    args = [(source: Surface, dest: Rect, area: Rect = None, special_flags: int = 0), ...]
    Draws a sequence of Surfaces onto this Surface...

    >>> surf.blits([(source, dest),
(source, dest),
(source, dest, area),
(source, dest, area, BLEND_ADD)]
    [Rect(), Rect(), Rect(), Rect()]

One potential option...
  • Have a return_rects=False argument, where if you pass it, then it can return None instead of a list of rects. This way you avoid allocating a list, and all the rects inside it. I'll benchmark this to see if it's worth it -- but I have a feeling all those allocations will be significant. But some people don't track updates, so allocating the rects is not worth it for them. eg. the implementation from Leif doesn't return rects.

It can handle these use cases:
  • blit many different surfaces to one surface (like the screen)
  • blit one surface many times to one surface.
  • when you don't care about rects, it doesn't allocate them.
  • when you do care about update tracking, it can track them.
It can *not* handle (but would anyone care?):
  • blit many surfaces, to many other surfaces.

Areas not included in the scope of this:
  • This could be used by two sprite groups quite easily (Group, RenderUpdates). But I think it's worth trying to compile the sprite groups with Cython instead, as a separate piece of work.
  • Multi processing. It should be possible to use this API to build a multi process blitter. However, this is not addressed in this work. The Surface we are blitting onto could be split up into numberOfCore tiles, and rendered that way. This is classic tile rendering, and nothing in this API stops an implementation of this later.

Enhancements, objections?


On Mon, Mar 27, 2017 at 6:38 PM, René Dudfield <renesd@xxxxxxxxx> wrote:
Cool about the blit function. Since the rect and surfaces are by reference, then you only need to make sprite_image_list once (if you are blitting the same thing every time). Additionally, I think it would likely be fast enough for another commonly requested case (blit the same image to many different spots). It would also work nicely for sprite groups. I think it's worth some more API discussion, before merging it in. We'd also need some tests and docs.

On Mon, Mar 27, 2017 at 4:59 PM, Leif Theden <leif.theden@xxxxxxxxx> wrote:
I think a function that accepts a sequence of tuples in the form of (dest_surface, dest_position, source_area, blend_mode) would be enough.  It is only needed as a power-user optimization and I don't seem much value in watering it down or splitting it into multiple functions for beginners.  No need to overthink and complicate it.  Cython may help sprite rendering in Groups, but it would have to be implemented and tested.

Has anyone considered releasing the GIL during blit operations?  Is it possible the release the GIL during a blit, then block when needed, in the cases of subsequent draw operations?  The optimized case would be to allow math operations to execute while the display buffer is being modified, while you are iterating over sprites.

Here is my preliminary C code for a "blit_multi" function.  It is called "blit_list".  It only supports (dest_surface, dest_position) tuples, but it works as so far.

Use it like this:

sprite_image_list = [(s.image, s.rect) for s in self.sprites]