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 http://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"""
 11
 12import random
 13import arcade
 14import os
 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 = 500
 25
 26STOP_COUNT = 10000
 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        # Set the working directory (where we expect to find files) to the same
 71        # directory this .py file is in. You can leave this out of your own
 72        # code, but it is needed to easily run the examples using "python -m"
 73        # as mentioned at the top of this program.
 74        file_path = os.path.dirname(os.path.abspath(__file__))
 75        os.chdir(file_path)
 76
 77        # Variables that will hold sprite lists
 78        self.coin_list = None
 79
 80        self.processing_time = 0
 81        self.draw_time = 0
 82        self.program_start_time = timeit.default_timer()
 83        self.sprite_count_list = []
 84        self.fps_list = []
 85        self.processing_time_list = []
 86        self.drawing_time_list = []
 87        self.last_fps_reading = 0
 88        self.fps = FPSCounter()
 89
 90        arcade.set_background_color(arcade.color.AMAZON)
 91
 92        # Open file to save timings
 93        self.results_file = open(RESULTS_FILE, "w")
 94
 95    def add_coins(self):
 96
 97        # Create the coins
 98        for i in range(COIN_COUNT_INCREMENT):
 99            # Create the coin instance
100            # Coin image from kenney.nl
101            coin = Coin(":resources:images/items/coinGold.png", SPRITE_SCALING_COIN)
102
103            # Position the coin
104            coin.center_x = random.randrange(SPRITE_SIZE, SCREEN_WIDTH - SPRITE_SIZE)
105            coin.center_y = random.randrange(SPRITE_SIZE, SCREEN_HEIGHT - SPRITE_SIZE)
106
107            coin.change_x = random.randrange(-3, 4)
108            coin.change_y = random.randrange(-3, 4)
109
110            # Add the coin to the lists
111            self.coin_list.append(coin)
112
113    def setup(self):
114        """ Set up the game and initialize the variables. """
115
116        # Sprite lists
117        self.coin_list = arcade.SpriteList(use_spatial_hash=False)
118
119    def on_draw(self):
120        """ Draw everything """
121
122        # Start timing how long this takes
123        draw_start_time = timeit.default_timer()
124
125        arcade.start_render()
126        self.coin_list.draw()
127
128        # Display info on sprites
129        # output = f"Sprite count: {len(self.coin_list):,}"
130        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 20, arcade.color.BLACK, 16)
131        #
132        # # Display timings
133        # output = f"Processing time: {self.processing_time:.3f}"
134        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 40, arcade.color.BLACK, 16)
135        #
136        # output = f"Drawing time: {self.draw_time:.3f}"
137        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 60, arcade.color.BLACK, 16)
138        #
139        # fps = self.fps.get_fps()
140        # output = f"FPS: {fps:3.0f}"
141        # arcade.draw_text(output, 20, SCREEN_HEIGHT - 80, arcade.color.BLACK, 16)
142
143        self.draw_time = timeit.default_timer() - draw_start_time
144        self.fps.tick()
145
146    def update(self, delta_time):
147        # Start update timer
148        start_time = timeit.default_timer()
149
150        self.coin_list.update()
151
152        for sprite in self.coin_list:
153
154            if sprite.position[0] < 0:
155                sprite.change_x *= -1
156            elif sprite.position[0] > SCREEN_WIDTH:
157                sprite.change_x *= -1
158            if sprite.position[1] < 0:
159                sprite.change_y *= -1
160            elif sprite.position[1] > SCREEN_HEIGHT:
161                sprite.change_y *= -1
162
163        # Save the time it took to do this.
164        self.processing_time = timeit.default_timer() - start_time
165
166        # Total time program has been running
167        total_program_time = int(timeit.default_timer() - self.program_start_time)
168
169        # Print out stats, or add more sprites
170        if total_program_time > self.last_fps_reading:
171            self.last_fps_reading = total_program_time
172
173            # It takes the program a while to "warm up", so the first
174            # few seconds our readings will be off. So wait some time
175            # before taking readings
176            if total_program_time > 5:
177
178                # We want the program to run for a while before taking
179                # timing measurements. We don't want the time it takes
180                # to add new sprites to be part of that measurement. So
181                # make sure we have a clear second of nothing but
182                # running the sprites, and not adding the sprites.
183                if total_program_time % 2 == 1:
184
185                    # Take timings
186                    output = f"{total_program_time}, {len(self.coin_list)}, {self.fps.get_fps():.1f}, " \
187                             f"{self.processing_time:.4f}, {self.draw_time:.4f}\n"
188
189                    self.results_file.write(output)
190                    print(output, end="")
191                    if len(self.coin_list) >= STOP_COUNT:
192                        pyglet.app.exit()
193                        return
194
195                    self.sprite_count_list.append(len(self.coin_list))
196                    self.fps_list.append(round(self.fps.get_fps(), 1))
197                    self.processing_time_list.append(self.processing_time)
198                    self.drawing_time_list.append(self.draw_time)
199
200                    # Now add the coins
201                    self.add_coins()
202
203
204def main():
205    """ Main method """
206    window = MyGame()
207    window.setup()
208    arcade.run()
209
210
211if __name__ == "__main__":
212    main()