| 9120e874 Andy McCall |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
#include "perf_budget.h" |
| 14 |
#include "support/harness.h" |
| 15 |
|
| 16 |
#include <stdio.h> |
| 17 |
#include <string.h> |
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
#define KHZ 8000 |
| 22 |
#define CYCLES_PER_FRAME 134400 |
| 23 |
#define SCANLINES 525 |
| 24 |
|
| 25 |
static void |
| 26 |
configure(void) |
| 27 |
{ |
| 28 |
perf_budget_zone_clear(); |
| 29 |
perf_budget_set_enabled(false); |
| 30 |
perf_budget_set_capacity(PERF_DEFAULT_CAPACITY); |
| 31 |
perf_budget_configure(KHZ, CYCLES_PER_FRAME, SCANLINES); |
| 32 |
perf_budget_set_target_fps(60.0f); |
| 33 |
perf_budget_set_idle_mode(PERF_IDLE_AUTO); |
| 34 |
perf_budget_set_enabled(true); |
| 35 |
perf_budget_reset(); |
| 36 |
} |
| 37 |
|
| 38 |
|
| 39 |
static void |
| 40 |
run_work(uint32_t clocks, int count) |
| 41 |
{ |
| 42 |
for (int i = 0; i < count; i++) |
| 43 |
perf_budget_step(clocks, (uint16_t)(0x2000 + i * 3), 0, false, 0); |
| 44 |
} |
| 45 |
|
| 46 |
|
| 47 |
|
| 48 |
static void |
| 49 |
run_spin(uint16_t base, int passes) |
| 50 |
{ |
| 51 |
for (int p = 0; p < passes; p++) { |
| 52 |
perf_budget_step(4, base, 0, false, 0); |
| 53 |
perf_budget_step(2, (uint16_t)(base + 3), 0, false, 0); |
| 54 |
perf_budget_step(3, (uint16_t)(base + 5), 0, false, 0); |
| 55 |
} |
| 56 |
} |
| 57 |
|
| 58 |
int |
| 59 |
main(void) |
| 60 |
{ |
| 61 |
|
| 62 |
{ |
| 63 |
configure(); |
| 64 |
check_eq(perf_budget_cycles_per_frame(), CYCLES_PER_FRAME, |
| 65 |
"a VGA frame at 8 MHz is 134,400 cycles"); |
| 66 |
check_eq(perf_budget_cycles_per_scanline(), 256, |
| 67 |
"which is 256 cycles per scanline over 525 lines"); |
| 68 |
check_eq(perf_budget_budget_cycles(), CYCLES_PER_FRAME, |
| 69 |
"and at 60 fps the budget is one frame's worth"); |
| 70 |
check_eq((uint32_t)perf_budget_frames_per_budget(), 1, |
| 71 |
"spanning a single vsync"); |
| 72 |
|
| 73 |
const float hz = perf_budget_vsync_hz(); |
| 74 |
check(hz > 59.0f && hz < 60.5f, "vsync is derived at ~59.5 Hz"); |
| 75 |
} |
| 76 |
|
| 77 |
|
| 78 |
{ |
| 79 |
configure(); |
| 80 |
perf_budget_set_target_fps(30.0f); |
| 81 |
check_eq((uint32_t)perf_budget_frames_per_budget(), 2, |
| 82 |
"30 fps lets the work span two vsyncs"); |
| 83 |
check_eq(perf_budget_budget_cycles(), CYCLES_PER_FRAME * 2, |
| 84 |
"so the budget is two frames' cycles"); |
| 85 |
|
| 86 |
perf_budget_set_target_fps(20.0f); |
| 87 |
check_eq((uint32_t)perf_budget_frames_per_budget(), 3, "20 fps spans three"); |
| 88 |
|
| 89 |
perf_budget_set_target_fps(60.0f); |
| 90 |
check_eq((uint32_t)perf_budget_frames_per_budget(), 1, "and 60 fps is back to one"); |
| 91 |
} |
| 92 |
|
| 93 |
|
| 94 |
{ |
| 95 |
configure(); |
| 96 |
run_work(4, 100); |
| 97 |
perf_budget_frame_end(0); |
| 98 |
|
| 99 |
perf_frame_t f; |
| 100 |
check(perf_budget_last_frame(&f), "a completed frame is retained"); |
| 101 |
check_eq(f.total, 400, "every cycle stepped is counted"); |
| 102 |
check_eq(f.idle, 0, "straight-line code is never idle"); |
| 103 |
check_eq(perf_frame_work(&f), 400, "so all of it is work"); |
| 104 |
check_eq(f.instructions, 100, "and the instructions are counted"); |
| 105 |
} |
| 106 |
|
| 107 |
|
| 108 |
{ |
| 109 |
configure(); |
| 110 |
run_work(4, 10); |
| 111 |
for (int i = 0; i < 10; i++) |
| 112 |
perf_budget_step(10, 0x3000, 0, true, 0); |
| 113 |
perf_budget_frame_end(0); |
| 114 |
|
| 115 |
perf_frame_t f; |
| 116 |
perf_budget_last_frame(&f); |
| 117 |
check_eq(f.idle, 100, "WAI cycles are idle"); |
| 118 |
check_eq(perf_frame_work(&f), 40, "and the rest is work"); |
| 119 |
} |
| 120 |
|
| 121 |
|
| 122 |
{ |
| 123 |
configure(); |
| 124 |
run_work(4, 10); |
| 125 |
run_spin(0x4000, 20); |
| 126 |
perf_budget_frame_end(0); |
| 127 |
|
| 128 |
perf_frame_t f; |
| 129 |
perf_budget_last_frame(&f); |
| 130 |
|
| 131 |
|
| 132 |
|
| 133 |
check(f.idle >= 9 * 16, "a store-free spin loop is mostly idle"); |
| 134 |
check(perf_frame_work(&f) < 40 + 9 * 4, |
| 135 |
"and only the run-up is charged as work"); |
| 136 |
} |
| 137 |
|
| 138 |
|
| 139 |
{ |
| 140 |
configure(); |
| 141 |
for (int p = 0; p < 20; p++) { |
| 142 |
perf_budget_step(4, 0x4000, 0, false, 0); |
| 143 |
perf_budget_step(2, 0x4003, 0, false, 0); |
| 144 |
perf_budget_note_store(); |
| 145 |
perf_budget_step(3, 0x4005, 0, false, 0); |
| 146 |
} |
| 147 |
perf_budget_frame_end(0); |
| 148 |
|
| 149 |
perf_frame_t f; |
| 150 |
perf_budget_last_frame(&f); |
| 151 |
check_eq(f.idle, 0, "a loop with a side effect is never idle"); |
| 152 |
check_eq(perf_frame_work(&f), 9 * 20, "all of it counts as work"); |
| 153 |
} |
| 154 |
|
| 155 |
|
| 156 |
{ |
| 157 |
configure(); |
| 158 |
for (int p = 0; p < 20; p++) { |
| 159 |
perf_budget_step(4, 0x5000, 0, false, 0); |
| 160 |
perf_budget_step(2, 0x5000 + PERF_SPIN_SPAN + 8, 0, false, 0); |
| 161 |
} |
| 162 |
perf_budget_frame_end(0); |
| 163 |
|
| 164 |
perf_frame_t f; |
| 165 |
perf_budget_last_frame(&f); |
| 166 |
check_eq(f.idle, 0, "a loop spanning more than PERF_SPIN_SPAN is not a spin"); |
| 167 |
} |
| 168 |
|
| 169 |
|
| 170 |
{ |
| 171 |
configure(); |
| 172 |
run_spin(0x4000, 20); |
| 173 |
for (int i = 0; i < 10; i++) |
| 174 |
perf_budget_step(5, (uint16_t)(0x9000 + i * 2), 0, false, 1); |
| 175 |
perf_budget_frame_end(0); |
| 176 |
|
| 177 |
perf_frame_t f; |
| 178 |
perf_budget_last_frame(&f); |
| 179 |
check_eq(f.irq, 50, "cycles at interrupt depth are attributed to the handler"); |
| 180 |
check(f.idle > 0, "the spin before it is still recognised"); |
| 181 |
check(f.idle <= f.total - 50, "but the handler's cycles are not idle"); |
| 182 |
} |
| 183 |
|
| 184 |
|
| 185 |
{ |
| 186 |
configure(); |
| 187 |
const int z = perf_budget_zone_add("wait", 0x6000, 0x6100, -1, true); |
| 188 |
check(z >= 0, "an idle zone can be defined"); |
| 189 |
|
| 190 |
|
| 191 |
for (int p = 0; p < 10; p++) { |
| 192 |
perf_budget_step(4, 0x6000, 0, false, 0); |
| 193 |
perf_budget_note_store(); |
| 194 |
perf_budget_step(3, 0x6002, 0, false, 0); |
| 195 |
} |
| 196 |
perf_budget_frame_end(0); |
| 197 |
|
| 198 |
perf_frame_t f; |
| 199 |
perf_budget_last_frame(&f); |
| 200 |
check_eq(f.idle, 70, "an idle zone declares its cycles waiting regardless"); |
| 201 |
check_eq(perf_frame_work(&f), 0, "leaving no work at all"); |
| 202 |
} |
| 203 |
|
| 204 |
|
| 205 |
{ |
| 206 |
configure(); |
| 207 |
const int z0 = perf_budget_zone_add("sprites", 0x2000, 0x2100, -1, false); |
| 208 |
const int z1 = perf_budget_zone_add("banked", 0xA000, 0xA100, 3, false); |
| 209 |
check(z0 == 0 && z1 == 1, "zones take the first free slots"); |
| 210 |
|
| 211 |
run_work(4, 4); |
| 212 |
perf_budget_step(7, 0xA000, 3, false, 0); |
| 213 |
perf_budget_step(9, 0xA000, 5, false, 0); |
| 214 |
perf_budget_frame_end(0); |
| 215 |
|
| 216 |
perf_frame_t f; |
| 217 |
perf_budget_last_frame(&f); |
| 218 |
check_eq(f.zone[0], 16, "a zone collects the cycles inside it"); |
| 219 |
check_eq(f.zone[1], 7, "a banked zone collects only its own bank"); |
| 220 |
check_eq(f.total, 32, "while the frame total counts everything"); |
| 221 |
} |
| 222 |
|
| 223 |
|
| 224 |
{ |
| 225 |
configure(); |
| 226 |
perf_budget_zone_add("gone", 0x2000, 0x2100, -1, false); |
| 227 |
check(perf_budget_zone_remove(0), "a zone can be removed"); |
| 228 |
check(!perf_budget_zone_get(0, NULL), "and is then unknown"); |
| 229 |
run_work(4, 4); |
| 230 |
perf_budget_frame_end(0); |
| 231 |
|
| 232 |
perf_frame_t f; |
| 233 |
perf_budget_last_frame(&f); |
| 234 |
check_eq(f.zone[0], 0, "cycles are no longer attributed to it"); |
| 235 |
} |
| 236 |
|
| 237 |
|
| 238 |
{ |
| 239 |
configure(); |
| 240 |
check_eq(perf_budget_marker_read(), PERF_MARKER_PROTOCOL, |
| 241 |
"the marker register reads back a protocol version"); |
| 242 |
|
| 243 |
perf_budget_marker_write(0xF0); |
| 244 |
run_work(4, 10); |
| 245 |
perf_budget_marker_write(0xF1); |
| 246 |
run_work(4, 10); |
| 247 |
perf_budget_note_store(); |
| 248 |
run_work(4, 10); |
| 249 |
perf_budget_frame_end(0); |
| 250 |
|
| 251 |
perf_frame_t f; |
| 252 |
perf_budget_last_frame(&f); |
| 253 |
check_eq(perf_frame_work(&f), 40, "only the marked span is work"); |
| 254 |
check_eq(f.idle, 80, "everything after the end marker is idle"); |
| 255 |
} |
| 256 |
|
| 257 |
|
| 258 |
{ |
| 259 |
configure(); |
| 260 |
perf_budget_marker_write(0xF0); |
| 261 |
perf_budget_marker_write(0x01); |
| 262 |
run_work(4, 5); |
| 263 |
perf_budget_marker_write(0x02); |
| 264 |
run_work(4, 5); |
| 265 |
perf_budget_marker_write(0x82); |
| 266 |
run_work(4, 5); |
| 267 |
perf_budget_marker_write(0x81); |
| 268 |
run_work(4, 5); |
| 269 |
perf_budget_frame_end(0); |
| 270 |
|
| 271 |
perf_frame_t f; |
| 272 |
perf_budget_last_frame(&f); |
| 273 |
check_eq(f.zone[0], 40, "an outer region collects its own cycles"); |
| 274 |
check_eq(f.zone[1], 20, "the innermost open region wins while nested"); |
| 275 |
|
| 276 |
perf_zone_t z; |
| 277 |
check(perf_budget_zone_get(0, &z), "a marker region claims a zone slot"); |
| 278 |
check(z.is_marker && z.marker_id == 1, "tagged with the region it stands for"); |
| 279 |
check(strcmp(z.name, "region 1") == 0, "and named after it"); |
| 280 |
} |
| 281 |
|
| 2980b7b6 Andy McCall ↶ |
282 |
|
| 9120e874 Andy McCall |
283 |
{ |
| 2980b7b6 Andy McCall ↶ |
284 |
|
| 285 |
|
| 286 |
|
| 9120e874 Andy McCall |
287 |
configure(); |
| 288 |
perf_budget_marker_write(0xF0); |
| 2980b7b6 Andy McCall ↶ |
289 |
perf_budget_marker_write(0x01); |
| 290 |
run_work(4, 10); |
| 291 |
perf_budget_marker_write(0x00); |
| 292 |
perf_budget_marker_write(0x02); |
| 293 |
run_work(4, 10); |
| 294 |
perf_budget_frame_end(0); |
| 295 |
run_work(4, 10); |
| 296 |
perf_budget_marker_write(0x00); |
| 297 |
perf_budget_marker_write(0xF1); |
| 298 |
run_work(4, 10); |
| 299 |
perf_budget_frame_end(0); |
| 300 |
|
| 301 |
perf_frame_t f; |
| 302 |
perf_budget_frame_at(1, &f); |
| 303 |
check_eq(f.zone[0], 40, "phase 1 before the boundary"); |
| 304 |
check_eq(f.zone[1], 40, "phase 2's cycles before the boundary"); |
| 305 |
perf_budget_last_frame(&f); |
| 306 |
check_eq(f.zone[1], 40, "and phase 2's after it, carried across"); |
| 307 |
check_eq(f.idle, 40, "the wait after $F1 idle"); |
| 308 |
} |
| 309 |
|
| 310 |
|
| 311 |
{ |
| 312 |
|
| 313 |
|
| 314 |
configure(); |
| 315 |
perf_budget_marker_write(0xF0); |
| 316 |
run_work(4, 10); |
| 317 |
perf_budget_marker_write(0xF1); |
| 318 |
for (int i = 0; i < 10; i++) perf_budget_step(4, 0x2000, 0, false, 0); |
| 319 |
for (int i = 0; i < 5; i++) perf_budget_step(6, 0xE000, 0, false, 1); |
| 320 |
perf_budget_frame_end(0); |
| 321 |
perf_frame_t f; |
| 322 |
perf_budget_last_frame(&f); |
| 323 |
check_eq(f.irq, 30, "the handler's cycles are IRQ"); |
| 324 |
check_eq(f.idle, 40, "only the waiting is idle"); |
| 325 |
check_eq(perf_frame_work(&f), 70, "the handler counted as work"); |
| 326 |
} |
| 327 |
|
| 328 |
|
| 329 |
{ |
| 330 |
configure(); |
| 331 |
const int zi = perf_budget_zone_add_marker("sound", 7); |
| 332 |
check(zi >= 0, "a named marker region claims a zone slot"); |
| 333 |
perf_zone_t z; |
| 334 |
check(perf_budget_zone_get(zi, &z) && z.is_marker && z.marker_id == 7 && |
| 335 |
!strcmp(z.name, "sound"), "under its name, standing for region 7"); |
| 336 |
perf_budget_marker_write(0xF0); |
| 337 |
perf_budget_marker_write(0x07); |
| 338 |
run_work(4, 5); |
| 339 |
perf_budget_marker_write(0xF1); |
| 340 |
perf_budget_frame_end(0); |
| 341 |
perf_frame_t f; |
| 342 |
perf_budget_last_frame(&f); |
| 343 |
check_eq(f.zone[zi], 20, "and the guest's region 7 lands in it"); |
| 344 |
check(perf_budget_zone_add_marker("x", 0) < 0 && perf_budget_zone_add_marker("x", 0x40) < 0, |
| 345 |
"ids outside $01-$3F are refused"); |
| 346 |
} |
| 347 |
|
| 348 |
|
| 349 |
{ |
| 350 |
|
| 351 |
|
| 352 |
configure(); |
| 9120e874 Andy McCall |
353 |
perf_budget_marker_write(0x05); |
| 354 |
run_work(4, 10); |
| 355 |
perf_budget_frame_end(0); |
| 356 |
|
| 357 |
run_work(4, 10); |
| 358 |
perf_budget_frame_end(0); |
| 359 |
|
| 360 |
perf_frame_t f; |
| 361 |
perf_budget_last_frame(&f); |
| 362 |
check_eq(f.zone[0], 0, "a region left open is closed by the frame boundary"); |
| 2980b7b6 Andy McCall ↶ |
363 |
|
| 364 |
|
| 365 |
|
| 366 |
configure(); |
| 367 |
perf_budget_marker_write(0xF0); |
| 368 |
perf_budget_marker_write(0x05); |
| 369 |
run_work(4, 10); |
| 370 |
perf_budget_frame_end(0); |
| 371 |
perf_budget_marker_write(0xF0); |
| 372 |
run_work(4, 10); |
| 373 |
perf_budget_frame_end(0); |
| 374 |
perf_budget_last_frame(&f); |
| 375 |
check_eq(f.zone[0], 0, "a framed guest's open region ends at its next $F0"); |
| 9120e874 Andy McCall |
376 |
|
| 377 |
|
| 378 |
|
| 379 |
perf_budget_marker_write(0x93); |
| 380 |
run_work(4, 5); |
| 381 |
perf_budget_frame_end(0); |
| 382 |
perf_budget_last_frame(&f); |
| 383 |
check_eq(f.total, 20, "an unmatched leave is ignored"); |
| 384 |
} |
| 385 |
|
| 386 |
|
| 387 |
{ |
| 388 |
|
| 389 |
|
| 390 |
|
| 391 |
configure(); |
| 392 |
perf_budget_marker_write(0xF0); |
| 393 |
run_work(4, 5); |
| 394 |
perf_budget_frame_end(0); |
| 395 |
|
| 396 |
perf_budget_reset(); |
| 397 |
run_spin(0x4000, 20); |
| 398 |
perf_budget_frame_end(0); |
| 399 |
|
| 400 |
perf_frame_t f; |
| 401 |
perf_budget_last_frame(&f); |
| 402 |
check(f.idle > 0, "a reset gives automatic detection back after markers"); |
| 403 |
|
| 404 |
|
| 405 |
perf_budget_reset(); |
| 406 |
perf_budget_marker_write(0xF1); |
| 407 |
run_work(4, 10); |
| 408 |
perf_budget_frame_end(0); |
| 409 |
perf_budget_last_frame(&f); |
| 410 |
check_eq(f.idle, 40, "while the next marker write takes control again"); |
| 411 |
} |
| 412 |
|
| 413 |
|
| 414 |
{ |
| 415 |
configure(); |
| 416 |
perf_budget_set_idle_mode(PERF_IDLE_NONE); |
| 417 |
run_spin(0x4000, 20); |
| 418 |
for (int i = 0; i < 5; i++) |
| 419 |
perf_budget_step(10, 0x3000, 0, true, 0); |
| 420 |
perf_budget_frame_end(0); |
| 421 |
|
| 422 |
perf_frame_t f; |
| 423 |
perf_budget_last_frame(&f); |
| 424 |
check_eq(f.idle, 0, "PERF_IDLE_NONE classifies nothing as waiting"); |
| 425 |
|
| 426 |
configure(); |
| 427 |
perf_budget_set_idle_mode(PERF_IDLE_MARKERS); |
| 428 |
run_spin(0x4000, 20); |
| 429 |
perf_budget_frame_end(0); |
| 430 |
perf_budget_last_frame(&f); |
| 431 |
check_eq(f.idle, 0, "PERF_IDLE_MARKERS ignores the spin heuristic"); |
| 432 |
} |
| 433 |
|
| 434 |
|
| 435 |
{ |
| 436 |
configure(); |
| 437 |
|
| 438 |
const uint32_t under = CYCLES_PER_FRAME / 2; |
| 439 |
const uint32_t over = CYCLES_PER_FRAME + 1000; |
| 440 |
const uint32_t counts[5] = {under, over, under, over, under}; |
| 441 |
for (int i = 0; i < 5; i++) { |
| 442 |
perf_budget_step(counts[i], (uint16_t)(0x2000 + i), 0, false, 0); |
| 443 |
perf_budget_frame_end(1000); |
| 444 |
} |
| 445 |
|
| 446 |
perf_window_t w; |
| 447 |
check(perf_budget_window(0.0f, &w), "the whole ring is a valid window"); |
| 448 |
check_eq((uint32_t)w.frames, 5, "holding every frame recorded"); |
| 449 |
check_eq((uint32_t)w.overruns, 2, "two of which exceeded the budget"); |
| 450 |
check_eq(w.worst_overrun, 1000, "by 1000 cycles at worst"); |
| 451 |
check_eq(w.work.max, over, "the worst frame is the maximum"); |
| 452 |
check_eq(w.work.min, under, "and the best the minimum"); |
| 453 |
check(w.max_utilization > 100.0f, "so peak utilisation is over budget"); |
| 454 |
check(w.mean_utilization < 100.0f, "while the mean is under it"); |
| 455 |
} |
| 456 |
|
| 457 |
|
| 458 |
{ |
| 459 |
configure(); |
| 460 |
perf_overrun_report_t r; |
| 461 |
check(!perf_budget_drain_overruns(&r), "a clean run reports nothing"); |
| 462 |
|
| 463 |
perf_budget_step(CYCLES_PER_FRAME + 500, 0x2000, 0, false, 0); |
| 464 |
perf_budget_frame_end(0); |
| 465 |
perf_budget_step(CYCLES_PER_FRAME + 2500, 0x2000, 0, false, 0); |
| 466 |
perf_budget_frame_end(0); |
| 467 |
|
| 468 |
check(perf_budget_drain_overruns(&r), "overruns are reported once seen"); |
| 469 |
check_eq(r.overruns, 2, "coalescing every frame since the last drain"); |
| 470 |
check_eq(r.worst_overrun, 2500, "and keeping the worst of them"); |
| 471 |
check(!perf_budget_drain_overruns(&r), "draining clears the accumulator"); |
| 472 |
} |
| 473 |
|
| 474 |
|
| 475 |
{ |
| 476 |
configure(); |
| 477 |
|
| 478 |
for (int i = 1; i <= 100; i++) { |
| 479 |
perf_budget_step((uint32_t)i * 1000, 0x2000, 0, false, 0); |
| 480 |
perf_budget_frame_end(0); |
| 481 |
} |
| 482 |
perf_window_t w; |
| 483 |
check(perf_budget_window(0.0f, &w), "a hundred frames make a window"); |
| 484 |
check_eq((uint32_t)w.frames, 100, "all of them retained"); |
| 485 |
check_eq(w.work.min, 1000, "minimum is the smallest sample"); |
| 486 |
check_eq(w.work.max, 100000, "maximum is the largest"); |
| 487 |
check_eq(w.work.mean, 50500, "the mean is the average of 1..100 thousand"); |
| 488 |
check_eq(w.work.p50, 50000, "nearest-rank p50 over 100 samples"); |
| 489 |
check_eq(w.work.p95, 95000, "p95"); |
| 490 |
check_eq(w.work.p99, 99000, "p99"); |
| 491 |
} |
| 492 |
|
| 493 |
|
| 494 |
{ |
| 495 |
configure(); |
| 496 |
check(perf_budget_set_capacity(8), "the ring can be resized"); |
| 497 |
check_eq((uint32_t)perf_budget_capacity(), 8, "to the size asked for"); |
| 498 |
|
| 499 |
for (int i = 0; i < 20; i++) { |
| 500 |
perf_budget_step((uint32_t)(i + 1) * 100, 0x2000, 0, false, 0); |
| 501 |
perf_budget_frame_end(0); |
| 502 |
} |
| 503 |
check_eq((uint32_t)perf_budget_frame_count(), 8, "which then holds eight frames"); |
| 504 |
check_eq(perf_budget_total_frames(), 20, "having seen twenty"); |
| 505 |
|
| 506 |
perf_frame_t f; |
| 507 |
perf_budget_last_frame(&f); |
| 508 |
check_eq(f.total, 2000, "the newest frame survives the wrap"); |
| 509 |
check_eq(f.frame, 19, "with its original ordinal"); |
| 510 |
|
| 511 |
perf_window_t w; |
| 512 |
perf_budget_window(0.0f, &w); |
| 513 |
check_eq(w.work.min, 1300, "and the window holds only the last eight"); |
| 514 |
check_eq(w.work.max, 2000, "up to the newest"); |
| 515 |
} |
| 516 |
|
| 517 |
|
| 518 |
{ |
| 519 |
configure(); |
| 520 |
for (int i = 0; i < 600; i++) { |
| 521 |
perf_budget_step(1000, 0x2000, 0, false, 0); |
| 522 |
perf_budget_frame_end(0); |
| 523 |
} |
| 524 |
perf_window_t w; |
| 525 |
check(perf_budget_window(1.0f, &w), "one second is a valid window"); |
| 526 |
check(w.frames >= 55 && w.frames <= 65, |
| 527 |
"holding about one vsync period's worth of frames"); |
| 528 |
|
| 529 |
check(perf_budget_window(5.0f, &w), "and so is five seconds"); |
| 530 |
check(w.frames >= 290 && w.frames <= 305, "with about five times as many"); |
| 531 |
} |
| 532 |
|
| 533 |
|
| 534 |
{ |
| 535 |
configure(); |
| 536 |
perf_budget_set_target_fps(30.0f); |
| 537 |
|
| 538 |
|
| 539 |
|
| 540 |
const uint32_t each = CYCLES_PER_FRAME * 3 / 4; |
| 541 |
for (int i = 0; i < 6; i++) { |
| 542 |
perf_budget_step(each, 0x2000, 0, false, 0); |
| 543 |
perf_budget_frame_end(0); |
| 544 |
} |
| 545 |
perf_window_t w; |
| 546 |
perf_budget_window(0.0f, &w); |
| 547 |
check_eq((uint32_t)w.overruns, 0, "3/4 of a frame each is within a 30 fps budget"); |
| 548 |
check_eq(w.budget_work.max, each * 2, "which is measured over two frames"); |
| 549 |
check(w.max_utilization > 74.0f && w.max_utilization < 76.0f, |
| 550 |
"putting utilisation at about 75%"); |
| 551 |
|
| 552 |
|
| 553 |
configure(); |
| 554 |
perf_budget_set_target_fps(30.0f); |
| 555 |
for (int i = 0; i < 6; i++) { |
| 556 |
perf_budget_step(CYCLES_PER_FRAME + 5000, 0x2000, 0, false, 0); |
| 557 |
perf_budget_frame_end(0); |
| 558 |
} |
| 559 |
perf_budget_window(0.0f, &w); |
| 560 |
check(w.overruns > 0, "but a full frame of work each is not"); |
| 561 |
} |
| 562 |
|
| 563 |
|
| 564 |
{ |
| 565 |
|
| 566 |
|
| 567 |
|
| 568 |
|
| 569 |
|
| 570 |
configure(); |
| 571 |
for (int outer = 0; outer < 20; outer++) { |
| 572 |
|
| 573 |
for (int i = 0; i < 8; i++) { |
| 574 |
perf_budget_step(4, 0xC004, 0, false, 0); |
| 575 |
perf_budget_note_store(); |
| 576 |
perf_budget_step(2, 0xC007, 0, false, 0); |
| 577 |
perf_budget_step(3, 0xC008, 0, false, 0); |
| 578 |
} |
| 579 |
|
| 580 |
for (int i = 0; i < 8; i++) { |
| 581 |
perf_budget_step(2, 0xC00C, 0, false, 0); |
| 582 |
perf_budget_step(2, 0xC00D, 0, false, 0); |
| 583 |
perf_budget_step(3, 0xC00E, 0, false, 0); |
| 584 |
} |
| 585 |
perf_budget_step(3, 0xC010, 0, false, 0); |
| 586 |
} |
| 587 |
perf_budget_frame_end(0); |
| 588 |
|
| 589 |
perf_frame_t f; |
| 590 |
perf_budget_last_frame(&f); |
| 591 |
const float idle_pct = 100.0f * (float)f.idle / (float)f.total; |
| 592 |
check(idle_pct > 20.0f && idle_pct < 70.0f, |
| 593 |
"a store-free poll nested in a compact storing routine is still found"); |
| 594 |
check(f.idle > 0, "and its cycles are credited as waiting"); |
| 595 |
} |
| 596 |
|
| 597 |
|
| 598 |
{ |
| 599 |
configure(); |
| 600 |
|
| 601 |
perf_budget_set_target_fps(120.0f); |
| 602 |
check_eq((uint32_t)perf_budget_frames_per_budget(), 1, |
| 603 |
"a sub-frame target still measures over one frame"); |
| 604 |
const uint32_t half = perf_budget_budget_cycles(); |
| 605 |
check(half > CYCLES_PER_FRAME * 45 / 100 && half < CYCLES_PER_FRAME * 55 / 100, |
| 606 |
"and gets about half a frame's cycles"); |
| 607 |
|
| 608 |
|
| 609 |
|
| 610 |
|
| 611 |
|
| 612 |
for (int i = 0; i < 5; i++) { |
| 613 |
perf_budget_step(CYCLES_PER_FRAME * 3 / 4, 0x2000, 0, false, 0); |
| 614 |
perf_budget_frame_end(0); |
| 615 |
} |
| 616 |
perf_window_t w; |
| 617 |
perf_budget_window(0.0f, &w); |
| 618 |
check_eq((uint32_t)w.overruns, 5, "three quarters of a frame overruns a half-frame budget"); |
| 619 |
check(w.max_utilization > 140.0f, "at around 150% utilisation"); |
| 620 |
} |
| 621 |
|
| 622 |
|
| 623 |
{ |
| 624 |
configure(); |
| 625 |
|
| 626 |
|
| 627 |
perf_budget_set_target_fps(60.0f); |
| 628 |
check_eq(perf_budget_budget_cycles(), CYCLES_PER_FRAME, |
| 629 |
"a 60 fps target snaps onto the 59.52 Hz frame"); |
| 630 |
perf_budget_set_target_fps(59.0f); |
| 631 |
check_eq(perf_budget_budget_cycles(), CYCLES_PER_FRAME, |
| 632 |
"and so does 59"); |
| 633 |
} |
| 634 |
|
| 635 |
|
| 636 |
{ |
| 637 |
configure(); |
| 638 |
|
| 639 |
|
| 640 |
|
| 641 |
for (int i = 0; i < 10; i++) { |
| 642 |
perf_budget_step(CYCLES_PER_FRAME + 3, 0x2000, 0, false, 0); |
| 643 |
perf_budget_frame_end(0); |
| 644 |
} |
| 645 |
perf_window_t w; |
| 646 |
perf_budget_window(0.0f, &w); |
| 647 |
check_eq((uint32_t)w.overruns, 0, |
| 648 |
"overshooting the frame by one instruction is not an overrun"); |
| 649 |
|
| 650 |
|
| 651 |
configure(); |
| 652 |
for (int i = 0; i < 10; i++) { |
| 653 |
perf_budget_step(CYCLES_PER_FRAME + 4000, 0x2000, 0, false, 0); |
| 654 |
perf_budget_frame_end(0); |
| 655 |
} |
| 656 |
perf_budget_window(0.0f, &w); |
| 657 |
check_eq((uint32_t)w.overruns, 10, "but a real overshoot still is"); |
| 658 |
} |
| 659 |
|
| 660 |
|
| 661 |
{ |
| 662 |
configure(); |
| 663 |
perf_budget_set_enabled(false); |
| 664 |
check(!perf_budget_is_enabled(), "profiling can be switched off"); |
| 665 |
run_work(4, 100); |
| 666 |
perf_budget_frame_end(0); |
| 667 |
|
| 668 |
perf_frame_t f; |
| 669 |
check(!perf_budget_last_frame(&f), "and then records nothing"); |
| 670 |
|
| 671 |
perf_window_t w; |
| 672 |
check(!perf_budget_window(0.0f, &w), "with no window to report"); |
| 673 |
} |
| 674 |
|
| 675 |
|
| 676 |
{ |
| 677 |
configure(); |
| 678 |
perf_budget_set_target_fps(30.0f); |
| 679 |
run_work(4, 10); |
| 680 |
perf_budget_frame_end(0); |
| 681 |
perf_budget_reset(); |
| 682 |
|
| 683 |
check_eq((uint32_t)perf_budget_frame_count(), 0, "reset empties the ring"); |
| 684 |
check_eq(perf_budget_total_frames(), 0, "and the frame ordinal"); |
| 685 |
check_eq((uint32_t)perf_budget_frames_per_budget(), 2, |
| 686 |
"while leaving the target fps alone"); |
| 687 |
|
| 688 |
perf_budget_set_enabled(false); |
| 689 |
} |
| 690 |
|
| 691 |
return x16_test_summary("perf_budget"); |
| 692 |
} |