The drawing thread should handle all blits to the screen as well as flip.
But in a framework we cannot prevent users creating surfaces and mutating
them in the logic thread.

On Mon, 18 Jun 2018, 09:55 Radomir Dopieralski, <pyg...@sheep.art.pl> wrote:

> Them again, if you assume that the drawing thread is doing all the
> blits, then the situation is reversed — it's the only thread that needs
> write access to the surfaces, and everything is good again. In your
> scenario, we still get the correct result, because all the blits are
> executed by the drawing thread in the same order in which they were
> scheduled — including the blits that modify your temporary surface with
> the digits. As long as all the modifications happen either on one side
> or the other, we are good.
>
> On Mon, 18 Jun 2018 01:09:16 +0200
> Radomir Dopieralski <pyg...@sheep.art.pl> wrote:
>
> > Oh, I think I misunderstood what the drawing thread was supposed to
> > do. I thought it would only handle the "flip" and/or "update" calls,
> > that is, sending the pixel data of the screen surface to the graphic
> > cards, which is the slowest part of any pygame program. But from what
> > you write, I understand it's supposed to handle all blits as well?
> >
> > On Sun, 17 Jun 2018 17:33:54 -0500
> > Luke Paireepinart <rabidpoob...@gmail.com> wrote:
> >
> > > Radomir, there is another side effect - if the surfaces are not
> > > copied, then whatever content was in surf a at the moment that it
> > > was supposed to be drawn now no longer exists. For example, say the
> > > logic thread is font rendering the numbers 1 thru 9, and then
> > > blitting these to positions that are not overlapping each other,
> > > but it is reusing a surface for the font render call. In the
> > > synchronous example, 1, 2, 3, etc would be drawn. Make that
> > > asynchronous and you could end up with 9,9,9,9,9....
> > >
> > > If you are multithreading a game, the "main" thread should be
> > > mutating the game state, not the surfaces directly. The render
> > > thread should be the view of the model state at the snapshot in
> > > time at which the render is called. The logic thread should have a
> > > control that is determining the time between updates and applying
> > > the appropriate amount of ticks to the game state / emulating
> > > physics / etc. Then whenever the previous draw has completed, the
> > > render thread would snapshot the state and represent it
> > > appropriately (either redrawing or comparing to its previous
> > > state). If there needs to be multiple draw calls to represent the
> > > state properly since the last state, then they can all be done
> > > before the display buffer is flipped.
> > >
> > > In the 1-9 example, each font would be represented by a game object
> > > that had a positional element, and you could add them to the map
> > > asynchronous of the draws since the render thread would display them
> > > on the next iteration of the render loop where they existed.
> > >
> > >
> > > On Sun, Jun 17, 2018, 3:49 PM Radomir Dopieralski
> > > <pyg...@sheep.art.pl> wrote:
> > >
> > > > Of course strictly speaking there is a difference in behavior,
> > > > however, from the practical point of view, the difference boils
> > > > down to the fact that something gets drawn half a frame earlier
> > > > rather than half a frame later (because the command to draw it was
> > > > given in between the frames). Unless you have less than 6 frames a
> > > > second, the difference wouldn't be easy to notice, especially
> > > > since it would be rather rare too, since you would need to get
> > > > very specific timings to even have it happen. I wonder if it's
> > > > worth the effort.
> > > >
> > > > On Sun, 17 Jun 2018 21:40:56 +0100
> > > > Daniel Pope <ma...@mauveweb.co.uk> wrote:
> > > >
> > > > > The mutatation would be on the logic thread side. Consider this:
> > > > >
> > > > > draw_to_screen(surf_a,  ...)
> > > > > if cond:
> > > > >     surf_a.blit(surf_b, ...)
> > > > > draw_to_screen(surf_a, ...)
> > > > >
> > > > > Currently draw_to_screen() is synchronous. I'd like it to queue
> > > > > the blit instead, to happen in another thread. If I just did
> > > > >
> > > > > def draw_to_screen(surf, ...):
> > > > >     draw_queue.put((surf, ...))
> > > > >
> > > > > then there's a race condition - surf_a may be drawn twice with
> > > > > the surf_b update.
> > > > >
> > > > > If draw_to_screen() is implemented like
> > > > >
> > > > > def draw_to_screen(surf, ...):
> > > > >      draw_queue.put((surf.copy(), ...))
> > > > >
> > > > > Then I get no change in behaviour, but I copy on every blit, on
> > > > > the logic thread, ie 2 copies per frame regardless of whether
> > > > > cond is True.
> > > > >
> > > > > Changing the implementation of copy to create a COW clone means
> > > > > that the buffer copy actually happens at this line, if it is
> > > > > hit:
> > > > >
> > > > > surf_a.blit(surf_b, ...)
> > > > >
> > > > > Which means that there's 1 copy if cond is True and 0 if cond is
> > > > > False.
> > > > >
> > > > > On Sun, 17 Jun 2018, 20:59 Radomir Dopieralski,
> > > > > <pyg...@sheep.art.pl> wrote:
> > > > >
> > > > > > On Sun, 17 Jun 2018 17:48:26 +0200
> > > > > > Daniel Pope <ma...@mauveweb.co.uk> wrote:
> > > > > >
> > > > > > > I have been thinking for some time about how to optimise
> > > > > > > Pygame Zero games on Raspberry Pi. Most Pi models have
> > > > > > > multiple cores and an obvious improvement is to parallelise.
> > > > > > > Logic has to be synchronous but I would like to offload
> > > > > > > painting the screen to a separate thread.
> > > > > > >
> > > > > > > The problem I would have is in passing references to mutable
> > > > > > > objects between threads. Primarily this applies to Surface
> > > > > > > objects, which are mutable and expensive to copy. If I have
> > > > > > > a draw thread that maintains a queue of screen blit
> > > > > > > operations, I want the queue to hold references to surface
> > > > > > > data that won't change even if I later mutate the surfaces
> > > > > > > in the logic thread.
> > > > > >
> > > > > > Sorry if I am missing something obvious, but it seems to me
> > > > > > that the draw thread doesn't need to mutate the surfaces? I
> > > > > > mean, it only accesses them in a read-only fashion to render
> > > > > > them. So you don't need to pass references to mutable objects
> > > > > > — the drawing thread can get a read-only reference. Why do you
> > > > > > need it to have a reference to non-changing data? After all,
> > > > > > if it changes, you will have to re-draw it in the next frame
> > > > > > anyways. Unless the drawing thread is more than one frame
> > > > > > behind (which really shouldn't happen), you don't care about
> > > > > > the data changing.
>
> --
> Radomir Dopieralski
>

Reply via email to