@@ -45,6 +45,7 @@ def __init__(self, renderer, num_planes=1, allow_wrapping=True):
4545 self .vid_size = 0
4646 self .vid_cache = RAM ()
4747 self .ram_banks = []
48+ self .frame_delta = {}
4849 self .report_perf ()
4950
5051 for _ in range (num_planes ):
@@ -85,9 +86,7 @@ def clear(self):
8586 for plane in self .affect_planes :
8687 plane .clear ()
8788
88- for y in range (self .vid_height ):
89- for x in range (self .vid_width ):
90- self ._render_pixel (x , y )
89+ self ._redraw_all ()
9190
9291 def get_affected_planes (self ):
9392 return self .affect_planes
@@ -120,7 +119,7 @@ def _render_pixel(self, x, y):
120119 colour += 2 ** plane_num
121120
122121 if self .vid_cache .read (vram_loc ) != colour :
123- self .renderer . set_pixel (x , y , colour )
122+ self .frame_delta [ (x , y )] = colour
124123 self .vid_cache .write (vram_loc , colour )
125124
126125 # Half-pixel vertical scrolling is unsupported in 64x32 pixel mode
@@ -137,7 +136,7 @@ def scroll_up(self, rows):
137136 plane .move_mem (- mem_offset ) # Usually moves contents up by 1 pixel, or 0.5 on low resolution
138137 plane .zero_block (vid_size - mem_offset , mem_offset ) # Erase the bottom strips
139138
140- self ._post_scroll ()
139+ self ._redraw_all ()
141140
142141 def scroll_left (self , cols ):
143142 if not self .affect_planes :
@@ -153,7 +152,7 @@ def scroll_left(self, cols):
153152 # Erase 4-pixel block to right of line in high resolution, 2 on low resolution
154153 plane .zero_block (vid_width * y - cols , cols )
155154
156- self ._post_scroll ()
155+ self ._redraw_all ()
157156
158157 def scroll_right (self , cols ):
159158 if not self .affect_planes :
@@ -169,7 +168,7 @@ def scroll_right(self, cols):
169168 # Erase 4-pixel block to left of line in high resolution, 2 on low resolution
170169 plane .zero_block (vid_width * y , cols )
171170
172- self ._post_scroll ()
171+ self ._redraw_all ()
173172
174173 def scroll_down (self , rows ):
175174 if not self .affect_planes :
@@ -181,16 +180,23 @@ def scroll_down(self, rows):
181180 plane .move_mem (mem_offset ) # Usually moves contents down by 1 pixel, 0.5 on low resolution
182181 plane .zero_block (0 , mem_offset ) # Erase the top strips
183182
184- self ._post_scroll ()
183+ self ._redraw_all ()
185184
186- def _post_scroll (self ):
185+ def _redraw_all (self ):
187186 # Redraw whole screen after a scroll. The video cache should take the load off the renderer a bit
188187 for y in range (self .vid_height ):
189188 for x in range (self .vid_width ):
190189 self ._render_pixel (x , y )
191190
192191 def refresh_display (self ):
193- self .renderer .refresh_display ()
192+ # Request the renderer updates altered pixels and then refreshes the display. This method results in a huge
193+ # (around 5x) speed up when using PyPy with graphically-intensive games, and a tiny improvement with CPython.
194+ for xy , colour in self .frame_delta .items ():
195+ self .renderer .set_pixel (* xy , colour )
196+
197+ content_changed = bool (self .frame_delta )
198+ self .frame_delta .clear ()
199+ self .renderer .refresh_display (content_changed )
194200
195201 def switch_planes (self , mask ):
196202 try :
0 commit comments