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