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 >