| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
#include "vera_bandwidth.h" |
| 9 |
|
| 10 |
#include <stdlib.h> |
| 11 |
#include <string.h> |
| 12 |
|
| 13 |
bool vera_bandwidth_active = false; |
| 14 |
|
| 15 |
|
| 16 |
static bool owners[VERA_BW_OWNER_COUNT]; |
| 17 |
|
| 18 |
static uint16_t scanlines_per_frame = 525; |
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
|
| 23 |
static vera_bw_line_t cur_lines[VERA_BW_LINES]; |
| 24 |
static vera_bw_line_t pub_lines[VERA_BW_LINES]; |
| 25 |
|
| 26 |
static vera_bw_frame_t cur_frame; |
| 27 |
static vera_bw_frame_t pub_frame; |
| 28 |
static bool have_frame = false; |
| 29 |
|
| 30 |
|
| 31 |
|
| 32 |
|
| 33 |
|
| 34 |
uint8_t |
| 35 |
vera_bandwidth_pixels_per_word(bool bitmap_mode, uint8_t color_depth, uint8_t tile_width) |
| 36 |
{ |
| 37 |
const uint8_t mode = (uint8_t)((bitmap_mode ? 4 : 0) | (color_depth & 3)); |
| 38 |
switch (mode) { |
| 39 |
case 0: return tile_width ? 16 : 8; |
| 40 |
case 1: return tile_width ? 16 : 8; |
| 41 |
case 2: return 8; |
| 42 |
case 3: return 4; |
| 43 |
case 4: return 32; |
| 44 |
case 5: return 16; |
| 45 |
case 6: return 8; |
| 46 |
default: return 4; |
| 47 |
} |
| 48 |
} |
| 49 |
|
| 50 |
|
| 51 |
|
| 52 |
uint8_t |
| 53 |
vera_bandwidth_words_per_tile_line(bool bitmap_mode, uint8_t color_depth, uint8_t tile_width) |
| 54 |
{ |
| 55 |
const uint8_t mode = (uint8_t)((bitmap_mode ? 4 : 0) | (color_depth & 3)); |
| 56 |
switch (mode) { |
| 57 |
case 0: return 1; |
| 58 |
case 1: return 1; |
| 59 |
case 2: return tile_width ? 2 : 1; |
| 60 |
case 3: return tile_width ? 4 : 2; |
| 61 |
default: return 1; |
| 62 |
} |
| 63 |
} |
| 64 |
|
| 65 |
|
| 66 |
|
| 67 |
|
| 68 |
|
| 69 |
|
| 70 |
|
| 71 |
|
| 72 |
|
| 73 |
uint16_t |
| 74 |
vera_bandwidth_sprite_bus_clocks(uint16_t sprite_width, uint8_t color_mode) |
| 75 |
{ |
| 76 |
const uint16_t per_word = (uint16_t)(color_mode ? 4 : 8); |
| 77 |
const uint16_t words = (uint16_t)((sprite_width + per_word - 1) / per_word); |
| 78 |
return (uint16_t)(words * VERA_BW_ACCESS_CLOCKS); |
| 79 |
} |
| 80 |
|
| 81 |
uint16_t |
| 82 |
vera_bandwidth_layer_line_fetches(const vera_bw_layer_t *layer) |
| 83 |
{ |
| 84 |
if (layer == NULL || !layer->enabled) |
| 85 |
return 0; |
| 86 |
|
| 87 |
const uint8_t depth = (uint8_t)(layer->color_depth & 3); |
| 88 |
const uint8_t tw = layer->tile_width ? 1 : 0; |
| 89 |
|
| 90 |
if (layer->bitmap_mode) { |
| 91 |
|
| 92 |
|
| 93 |
|
| 94 |
|
| 95 |
|
| 96 |
|
| 97 |
|
| 98 |
const uint8_t ppw = vera_bandwidth_pixels_per_word(true, depth, tw); |
| 99 |
return (uint16_t)(VERA_BW_LAYER_PIXELS / ppw); |
| 100 |
} |
| 101 |
|
| 102 |
|
| 103 |
|
| 104 |
|
| 105 |
const uint8_t tile_px = (uint8_t)(tw ? 16 : 8); |
| 106 |
const uint16_t tiles = (uint16_t)((VERA_BW_LAYER_PIXELS / tile_px) + (layer->scrolled ? 1 : 0)); |
| 107 |
|
| 108 |
|
| 109 |
|
| 110 |
|
| 111 |
|
| 112 |
|
| 113 |
const uint16_t map_fetches = (uint16_t)((tiles + 1) / 2); |
| 114 |
const uint16_t data_fetches = (uint16_t)(tiles * vera_bandwidth_words_per_tile_line(false, depth, tw)); |
| 115 |
|
| 116 |
return (uint16_t)(map_fetches + data_fetches); |
| 117 |
} |
| 118 |
|
| 119 |
|
| 120 |
|
| 121 |
void |
| 122 |
vera_bandwidth_line_(uint16_t line, const vera_bw_layer_t *l0, const vera_bw_layer_t *l1) |
| 123 |
{ |
| 124 |
if (line >= VERA_BW_LINES) |
| 125 |
return; |
| 126 |
|
| 127 |
vera_bw_line_t *s = &cur_lines[line]; |
| 128 |
|
| 129 |
|
| 130 |
|
| 131 |
|
| 132 |
|
| 133 |
|
| 134 |
|
| 135 |
s->layer_fetches[0] = vera_bandwidth_layer_line_fetches(l0); |
| 136 |
s->layer_fetches[1] = vera_bandwidth_layer_line_fetches(l1); |
| 137 |
s->rendered = true; |
| 138 |
} |
| 139 |
|
| 140 |
void |
| 141 |
vera_bandwidth_note_port_(bool write, uint8_t vram_bytes, uint16_t line) |
| 142 |
{ |
| 143 |
if (write) |
| 144 |
cur_frame.port_writes++; |
| 145 |
else |
| 146 |
cur_frame.port_reads++; |
| 147 |
|
| 148 |
cur_frame.port_accesses++; |
| 149 |
cur_frame.port_vram_bytes += vram_bytes; |
| 150 |
|
| 151 |
if (line < VERA_BW_LINES) |
| 152 |
cur_lines[line].port_accesses++; |
| 153 |
} |
| 154 |
|
| 155 |
void |
| 156 |
vera_bandwidth_note_fx_cache_write_(uint16_t line) |
| 157 |
{ |
| 158 |
(void)line; |
| 159 |
cur_frame.fx_cache_writes++; |
| 160 |
} |
| 161 |
|
| 162 |
void |
| 163 |
vera_bandwidth_note_fx_affine_(uint16_t line) |
| 164 |
{ |
| 165 |
|
| 166 |
|
| 167 |
cur_frame.fx_affine_fetches++; |
| 168 |
cur_frame.port_accesses++; |
| 169 |
if (line < VERA_BW_LINES) |
| 170 |
cur_lines[line].port_accesses++; |
| 171 |
} |
| 172 |
|
| 173 |
|
| 174 |
|
| 175 |
static int |
| 176 |
cmp_u16(const void *a, const void *b) |
| 177 |
{ |
| 178 |
const uint16_t x = *(const uint16_t *)a; |
| 179 |
const uint16_t y = *(const uint16_t *)b; |
| 180 |
return (x > y) - (x < y); |
| 181 |
} |
| 182 |
|
| 183 |
|
| 184 |
|
| 185 |
|
| 186 |
|
| 187 |
|
| 188 |
static void |
| 189 |
summarise(vera_bw_frame_t *f, const vera_bw_line_t *lines) |
| 190 |
{ |
| 191 |
static uint16_t sorted[VERA_BW_LINES]; |
| 192 |
uint16_t n = 0; |
| 193 |
|
| 194 |
f->layer_fetches[0] = f->layer_fetches[1] = 0; |
| 195 |
f->layer_peak_line[0] = f->layer_peak_line[1] = 0; |
| 196 |
f->layer_peak_fetches[0] = f->layer_peak_fetches[1] = 0; |
| 197 |
f->peak_line = f->peak_clocks = 0; |
| 198 |
f->total_clocks = f->total_fetches = 0; |
| 199 |
f->lines_over = 0; |
| 200 |
|
| 201 |
for (uint16_t i = 0; i < VERA_BW_LINES; i++) { |
| 202 |
const vera_bw_line_t *s = &lines[i]; |
| 203 |
|
| 204 |
|
| 205 |
|
| 206 |
|
| 207 |
if (!s->rendered && s->port_accesses == 0) |
| 208 |
continue; |
| 209 |
|
| 210 |
const uint32_t fetches = (uint32_t)s->layer_fetches[0] + s->layer_fetches[1] + s->port_accesses; |
| 211 |
const uint32_t clocks = vera_bw_line_clocks(s); |
| 212 |
|
| 213 |
for (int l = 0; l < 2; l++) { |
| 214 |
f->layer_fetches[l] += s->layer_fetches[l]; |
| 215 |
if (s->layer_fetches[l] > f->layer_peak_fetches[l]) { |
| 216 |
f->layer_peak_fetches[l] = s->layer_fetches[l]; |
| 217 |
f->layer_peak_line[l] = i; |
| 218 |
} |
| 219 |
} |
| 220 |
|
| 221 |
f->total_fetches += fetches; |
| 222 |
f->total_clocks += clocks; |
| 223 |
|
| 224 |
if (clocks > f->peak_clocks) { |
| 225 |
f->peak_clocks = (uint16_t)(clocks > 0xFFFF ? 0xFFFF : clocks); |
| 226 |
f->peak_line = i; |
| 227 |
} |
| 228 |
if (clocks > VERA_BW_LINE_CLOCKS) |
| 229 |
f->lines_over++; |
| 230 |
|
| 231 |
sorted[n++] = (uint16_t)(clocks > 0xFFFF ? 0xFFFF : clocks); |
| 232 |
} |
| 233 |
|
| 234 |
f->lines_active = n; |
| 235 |
f->layer_bytes[0] = f->layer_fetches[0] * VERA_BW_BYTES_PER_ACCESS; |
| 236 |
f->layer_bytes[1] = f->layer_fetches[1] * VERA_BW_BYTES_PER_ACCESS; |
| 237 |
|
| 238 |
if (n == 0) { |
| 239 |
f->p95_clocks = f->mean_clocks = 0; |
| 240 |
return; |
| 241 |
} |
| 242 |
|
| 243 |
qsort(sorted, n, sizeof sorted[0], cmp_u16); |
| 244 |
|
| 245 |
|
| 246 |
|
| 247 |
f->p95_clocks = sorted[(95u * (n - 1u) + 99u) / 100u]; |
| 248 |
f->mean_clocks = (uint16_t)(f->total_clocks / n); |
| 249 |
} |
| 250 |
|
| 251 |
void |
| 252 |
vera_bandwidth_frame_end(uint32_t frame) |
| 253 |
{ |
| 254 |
if (!vera_bandwidth_active) |
| 255 |
return; |
| 256 |
|
| 257 |
cur_frame.frame = frame; |
| 258 |
summarise(&cur_frame, cur_lines); |
| 259 |
|
| 260 |
memcpy(&pub_frame, &cur_frame, sizeof pub_frame); |
| 261 |
memcpy(pub_lines, cur_lines, sizeof pub_lines); |
| 262 |
have_frame = true; |
| 263 |
|
| 264 |
memset(&cur_frame, 0, sizeof cur_frame); |
| 265 |
memset(cur_lines, 0, sizeof cur_lines); |
| 266 |
} |
| 267 |
|
| 268 |
|
| 269 |
|
| 270 |
void |
| 271 |
vera_bandwidth_set_enabled(bool enabled) |
| 272 |
{ |
| 273 |
if (enabled == vera_bandwidth_active) |
| 274 |
return; |
| 275 |
vera_bandwidth_active = enabled; |
| 276 |
vera_bandwidth_reset(); |
| 277 |
} |
| 278 |
|
| 279 |
bool |
| 280 |
vera_bandwidth_is_enabled(void) |
| 281 |
{ |
| 282 |
return vera_bandwidth_active; |
| 283 |
} |
| 284 |
|
| 285 |
void |
| 286 |
vera_bandwidth_arm(vera_bw_owner_t owner, bool want) |
| 287 |
{ |
| 288 |
if ((unsigned)owner >= VERA_BW_OWNER_COUNT) |
| 289 |
return; |
| 290 |
owners[owner] = want; |
| 291 |
|
| 292 |
bool any = false; |
| 293 |
for (int i = 0; i < VERA_BW_OWNER_COUNT; i++) |
| 294 |
any = any || owners[i]; |
| 295 |
|
| 296 |
vera_bandwidth_set_enabled(any); |
| 297 |
} |
| 298 |
|
| 299 |
bool |
| 300 |
vera_bandwidth_owner_wants(vera_bw_owner_t owner) |
| 301 |
{ |
| 302 |
return (unsigned)owner < VERA_BW_OWNER_COUNT && owners[owner]; |
| 303 |
} |
| 304 |
|
| 305 |
void |
| 306 |
vera_bandwidth_reset(void) |
| 307 |
{ |
| 308 |
memset(&cur_frame, 0, sizeof cur_frame); |
| 309 |
memset(&pub_frame, 0, sizeof pub_frame); |
| 310 |
memset(cur_lines, 0, sizeof cur_lines); |
| 311 |
memset(pub_lines, 0, sizeof pub_lines); |
| 312 |
have_frame = false; |
| 313 |
} |
| 314 |
|
| 315 |
void |
| 316 |
vera_bandwidth_configure(uint16_t lines) |
| 317 |
{ |
| 318 |
if (lines > 0) |
| 319 |
scanlines_per_frame = lines; |
| 320 |
} |
| 321 |
|
| 322 |
uint16_t |
| 323 |
vera_bandwidth_scanlines_per_frame(void) |
| 324 |
{ |
| 325 |
return scanlines_per_frame; |
| 326 |
} |
| 327 |
|
| 328 |
|
| 329 |
|
| 330 |
bool |
| 331 |
vera_bandwidth_last_frame(vera_bw_frame_t *out) |
| 332 |
{ |
| 333 |
if (!have_frame || out == NULL) |
| 334 |
return false; |
| 335 |
memcpy(out, &pub_frame, sizeof *out); |
| 336 |
return true; |
| 337 |
} |
| 338 |
|
| 339 |
bool |
| 340 |
vera_bandwidth_current_frame(vera_bw_frame_t *out) |
| 341 |
{ |
| 342 |
if (out == NULL || !vera_bandwidth_active) |
| 343 |
return false; |
| 344 |
memcpy(out, &cur_frame, sizeof *out); |
| 345 |
summarise(out, cur_lines); |
| 346 |
return true; |
| 347 |
} |
| 348 |
|
| 349 |
const vera_bw_line_t * |
| 350 |
vera_bandwidth_last_lines(uint16_t *count) |
| 351 |
{ |
| 352 |
if (!have_frame) { |
| 353 |
if (count) |
| 354 |
*count = 0; |
| 355 |
return NULL; |
| 356 |
} |
| 357 |
if (count) |
| 358 |
*count = VERA_BW_LINES; |
| 359 |
return pub_lines; |
| 360 |
} |