Draw Moving Sprites Stress Test#

Screenshot of stress test example
stress_test_draw_moving_arcade.py#
  1"""
  2Moving Sprite Stress Test
  3
  4Simple program to test how fast we can draw sprites that are moving
  5
  6Artwork from https://kenney.nl
  7
  8If Python and Arcade are installed, this example can be run from the command line with:
  9python -m arcade.examples.stress_test_draw_moving
 10"""
 11from __future__ import annotations
 12
 13import random
 14import arcade
 15import timeit
 16import time
 17import collections
 18import pyglet
 19
 20# --- Constants ---
 21SPRITE_SCALING_COIN = 0.25
 22SPRITE_NATIVE_SIZE = 128
 23SPRITE_SIZE = int(SPRITE_NATIVE_SIZE * SPRITE_SCALING_COIN)
 24COIN_COUNT_INCREMENT = 1000
 25
 26STOP_COUNT = 15000
 27RESULTS_FILE = "stress_test_draw_moving_arcade.csv"
 28
 29SCREEN_WIDTH = 1800
 30SCREEN_HEIGHT = 1000
 31SCREEN_TITLE = "Moving Sprite Stress Test"
 32
 33
 34class FPSCounter:
 35    def __init__(self):
 36        self.time = time.perf_counter()
 37        self.frame_times = collections.deque(maxlen=60)
 38
 39    def tick(self):
 40        t1 = time.perf_counter()
 41        dt = t1 - self.time
 42        self.time = t1
 43        self.frame_times.append(dt)
 44
 45    def get_fps(self):
 46        total_time = sum(self.frame_times)
 47        if total_time == 0:
 48            return 0
 49        else:
 50            return len(self.frame_times) / sum(self.frame_times)
 51
 52
 53class Coin(arcade.Sprite):
 54
 55    def update(self):
 56        """
 57        Update the sprite.
 58        """
 59        self.position = (self.position[0] + self.change_x, self.position[1] + self.change_y)
 60
 61
 62class MyGame(arcade.Window):
 63    """ Our custom Window Class"""
 64
 65    def __init__(self):
 66        """ Initializer """
 67        # Call the parent class initializer
 68        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
 69
 70        # Variables that will hold sprite lists
 71        self.coin_list = None
 72
 73        self.processing_time = 0
 74        self.draw_time = 0
 75        self.program_start_time = timeit.default_timer()
 76        self.sprite_count_list = []
 77        self.fps_list = []
 78        self.processing_time_list = []
 79        self.drawing_time_list = []
 80        self.last_fps_reading = 0
 81        self.fps = FPSCounter()
 82
 83        self.background_color = arcade.color.AMAZON
 84
 85        # Open file to save timings
 86        self.results_file = open(RESULTS_FILE, "w")
 87
 88    def add_coins(self):
 89
 90        # Create the coins
 91        for i in range(COIN_COUNT_INCREMENT):
 92            # Create the coin instance
 93            # Coin image from kenney.nl
 94            coin = Coin(":resources:images/items/coinGold.png", SPRITE_SCALING_COIN)
 95
 96            # Position the coin
 97            coin.center_x = random.randrange(SPRITE_SIZE, SCREEN_WIDTH - SPRITE_SIZE)
 98            coin.center_y = random.randrange(SPRITE_SIZE, SCREEN_HEIGHT - SPRITE_SIZE)
 99
100            coin.change_x = random.randrange(-3, 4)
101            coin.change_y = random.randrange(-3, 4)
102
103            # Add the coin to the lists
104            self.coin_list.append(coin)
105
106    def setup(self):
107        """ Set up the game and initialize the variables. """
108
109        # Sprite lists
110        self.coin_list = arcade.SpriteList(use_spatial_hash=False)
111
112    def on_draw(self):
113        """ Draw everything """
114
115        # Start timing how long this takes
116        draw_start_time = timeit.default_timer()
117
118        self.clear()
119        self.coin_list.draw()
120
121        # Display info on sprites
122        # output = f"Sprite count: {len(self.coin_list):,}"
123        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.BLACK, 16)
124        #
125        # # Display timings
126        # output = f"Processing time: {self.processing_time:.3f}"
127        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.BLACK, 16)
128        #
129        # output = f"Drawing time: {self.draw_time:.3f}"
130        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 60, arcade.color.BLACK, 16)
131        #
132        # fps = self.fps.get_fps()
133        # output = f"FPS: {fps:3.0f}"
134        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 80, arcade.color.BLACK, 16)
135
136        self.draw_time = timeit.default_timer() - draw_start_time
137        self.fps.tick()
138
139    def on_update(self, delta_time):
140        # Start update timer
141        start_time = timeit.default_timer()
142
143        self.coin_list.update()
144
145        for sprite in self.coin_list:
146
147            if sprite.position[0] < 0:
148                sprite.change_x *= -1
149            elif sprite.position[0] > SCREEN_WIDTH:
150                sprite.change_x *= -1
151            if sprite.position[1] < 0:
152                sprite.change_y *= -1
153            elif sprite.position[1] > SCREEN_HEIGHT:
154                sprite.change_y *= -1
155
156        # Save the time it took to do this.
157        self.processing_time = timeit.default_timer() - start_time
158
159        # Total time program has been running
160        total_program_time = int(timeit.default_timer() - self.program_start_time)
161
162        # Print out stats, or add more sprites
163        if total_program_time > self.last_fps_reading:
164            self.last_fps_reading = total_program_time
165
166            # It takes the program a while to "warm up", so the first
167            # few seconds our readings will be off. So wait some time
168            # before taking readings
169            if total_program_time > 5:
170
171                # We want the program to run for a while before taking
172                # timing measurements. We don't want the time it takes
173                # to add new sprites to be part of that measurement. So
174                # make sure we have a clear second of nothing but
175                # running the sprites, and not adding the sprites.
176                if total_program_time % 2 == 1:
177
178                    # Take timings
179                    output = f"{total_program_time}, {len(self.coin_list)}, {self.fps.get_fps():.1f}, " \
180                             f"{self.processing_time:.4f}, {self.draw_time:.4f}\n"
181
182                    self.results_file.write(output)
183                    print(output, end="")
184                    if len(self.coin_list) >= STOP_COUNT:
185                        pyglet.app.exit()
186                        return
187
188                    self.sprite_count_list.append(len(self.coin_list))
189                    self.fps_list.append(round(self.fps.get_fps(), 1))
190                    self.processing_time_list.append(self.processing_time)
191                    self.drawing_time_list.append(self.draw_time)
192
193                    # Now add the coins
194                    self.add_coins()
195
196
197def main():
198    """ Main function """
199    window = MyGame()
200    window.setup()
201    arcade.run()
202
203
204if __name__ == "__main__":
205    main()