From f702a425d2720594485e16df1f9c0b669129a639 Mon Sep 17 00:00:00 2001 From: Peter De Wachter Date: Wed, 30 Apr 2014 22:22:06 +0200 Subject: [PATCH] Simplify display damage tracking --- risc.c | 48 +++++++++++++++++++++++++---- risc.h | 9 ++++-- sdl-main.c | 89 +++++++++++++++++++++--------------------------------- 3 files changed, 83 insertions(+), 63 deletions(-) diff --git a/risc.c b/risc.c index 1ddf5a6..2d3ce93 100644 --- a/risc.c +++ b/risc.c @@ -10,8 +10,7 @@ #define MemWords (MemSize / 4) #define ROMStart 0x0FE000 #define ROMWords 512 -#define DisplayEnd 0x0FFF00 -#define DisplayStart (DisplayEnd - RISC_SCREEN_WIDTH * RISC_SCREEN_HEIGHT / 8) +#define DisplayStart 0x0E7F00 #define IOStart 0x0FFFC0 struct RISC { @@ -28,10 +27,11 @@ struct RISC { uint32_t leds; const struct RISC_Serial *serial; - uint32_t spi_selected; const struct RISC_SPI *sd_card; + struct Damage damage; + uint32_t RAM[MemWords]; uint32_t ROM[ROMWords]; }; @@ -60,6 +60,11 @@ static const uint32_t bootloader[ROMWords] = { struct RISC *risc_new() { struct RISC *risc = calloc(1, sizeof(*risc)); memcpy(risc->ROM, bootloader, sizeof(risc->ROM)); + risc->damage = (struct Damage){ + .x1 = 0, .y1 = 0, + .x2 = RISC_FRAMEBUFFER_WIDTH/32 - 1, + .y2 = RISC_FRAMEBUFFER_HEIGHT - 1 + }; risc_reset(risc); return risc; } @@ -311,9 +316,29 @@ static uint8_t risc_load_byte(struct RISC *risc, uint32_t address) { return (uint8_t)(w >> (address % 4 * 8)); } +static void risc_update_damage(struct RISC *risc, int w) { + int row = w / (RISC_FRAMEBUFFER_WIDTH / 32); + int col = w % (RISC_FRAMEBUFFER_WIDTH / 32); + if (col < risc->damage.x1) { + risc->damage.x1 = col; + } + if (col > risc->damage.x2) { + risc->damage.x2 = col; + } + if (row < risc->damage.y1) { + risc->damage.y1 = row; + } + if (row > risc->damage.y2) { + risc->damage.y2 = row; + } +} + static void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value) { - if (address < IOStart) { + if (address < DisplayStart) { risc->RAM[address/4] = value; + } else if (address < IOStart) { + risc->RAM[address/4] = value; + risc_update_damage(risc, address/4 - DisplayStart/4); } else { risc_store_io(risc, address, value); } @@ -321,11 +346,11 @@ static void risc_store_word(struct RISC *risc, uint32_t address, uint32_t value) static void risc_store_byte(struct RISC *risc, uint32_t address, uint8_t value) { if (address < IOStart) { - uint32_t w = risc->RAM[address/4]; + uint32_t w = risc_load_word(risc, address); uint32_t shift = (address & 3) * 8; w &= ~(0xFFu << shift); w |= (uint32_t)value << shift; - risc->RAM[address/4] = w; + risc_store_word(risc, address, w); } else { risc_store_io(risc, address, (uint32_t)value); } @@ -472,3 +497,14 @@ void risc_keyboard_input(struct RISC *risc, uint8_t *scancodes, uint32_t len) { uint32_t *risc_get_framebuffer_ptr(struct RISC *risc) { return &risc->RAM[DisplayStart/4]; } + +struct Damage risc_get_framebuffer_damage(struct RISC *risc) { + struct Damage dmg = risc->damage; + risc->damage = (struct Damage){ + .x1 = RISC_FRAMEBUFFER_WIDTH/32, + .x2 = 0, + .y1 = RISC_FRAMEBUFFER_HEIGHT, + .y2 = 0 + }; + return dmg; +} diff --git a/risc.h b/risc.h index 5128078..8ba4b8c 100644 --- a/risc.h +++ b/risc.h @@ -5,11 +5,15 @@ #include #include "risc-io.h" -#define RISC_SCREEN_WIDTH 1024 -#define RISC_SCREEN_HEIGHT 768 +#define RISC_FRAMEBUFFER_WIDTH 1024 +#define RISC_FRAMEBUFFER_HEIGHT 768 struct RISC; +struct Damage { + int x1, x2, y1, y2; +}; + struct RISC *risc_new(); void risc_set_serial(struct RISC *risc, const struct RISC_Serial *serial); void risc_set_spi(struct RISC *risc, int index, const struct RISC_SPI *spi); @@ -22,5 +26,6 @@ void risc_mouse_button(struct RISC *risc, int button, bool down); void risc_keyboard_input(struct RISC *risc, uint8_t *scancodes, uint32_t len); uint32_t *risc_get_framebuffer_ptr(struct RISC *risc); +struct Damage risc_get_framebuffer_damage(struct RISC *risc); #endif // RISC_H diff --git a/sdl-main.c b/sdl-main.c index bba9462..28ce1c7 100644 --- a/sdl-main.c +++ b/sdl-main.c @@ -20,8 +20,7 @@ static uint32_t BLACK = 0x657b83, WHITE = 0xfdf6e3; //static uint32_t BLACK = 0x000000, WHITE = 0x00FF00; -static void init_texture(SDL_Texture *texture); -static void update_texture(uint32_t *framebuffer, SDL_Texture *texture); +static void update_texture(struct RISC *risc, SDL_Texture *texture); static int clamp(int x, int min, int max); static double scale_display(SDL_Window *window, SDL_Rect *rect); @@ -69,9 +68,9 @@ int main (int argc, char *argv[]) { for (int i = 0; i < display_cnt; i++) { SDL_Rect bounds; SDL_GetDisplayBounds(i, &bounds); - if (bounds.w >= RISC_SCREEN_WIDTH && bounds.h == RISC_SCREEN_HEIGHT) { + if (bounds.w >= RISC_FRAMEBUFFER_WIDTH && bounds.h == RISC_FRAMEBUFFER_HEIGHT) { window_pos = SDL_WINDOWPOS_UNDEFINED_DISPLAY(i); - if (bounds.w == RISC_SCREEN_WIDTH) + if (bounds.w == RISC_FRAMEBUFFER_WIDTH) break; } } @@ -79,7 +78,8 @@ int main (int argc, char *argv[]) { SDL_Window *window = SDL_CreateWindow("Project Oberon", window_pos, window_pos, - RISC_SCREEN_WIDTH, RISC_SCREEN_HEIGHT, + RISC_FRAMEBUFFER_WIDTH, + RISC_FRAMEBUFFER_HEIGHT, window_flags); if (window == NULL) { fprintf(stderr, "Could not create window: %s\n", SDL_GetError()); @@ -95,7 +95,8 @@ int main (int argc, char *argv[]) { SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, - RISC_SCREEN_WIDTH, RISC_SCREEN_HEIGHT); + RISC_FRAMEBUFFER_WIDTH, + RISC_FRAMEBUFFER_HEIGHT); if (texture == NULL) { fprintf(stderr, "Could not create texture: %s\n", SDL_GetError()); return 1; @@ -103,7 +104,7 @@ int main (int argc, char *argv[]) { SDL_Rect display_rect; double display_scale = scale_display(window, &display_rect); - init_texture(texture); + update_texture(risc, texture); SDL_ShowWindow(window); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, &display_rect); @@ -131,14 +132,14 @@ int main (int argc, char *argv[]) { case SDL_MOUSEMOTION: { int scaled_x = (int)round((event.motion.x - display_rect.x) / display_scale); int scaled_y = (int)round((event.motion.y - display_rect.y) / display_scale); - int x = clamp(scaled_x, 0, RISC_SCREEN_WIDTH - 1); - int y = clamp(scaled_y, 0, RISC_SCREEN_HEIGHT - 1); + int x = clamp(scaled_x, 0, RISC_FRAMEBUFFER_WIDTH - 1); + int y = clamp(scaled_y, 0, RISC_FRAMEBUFFER_HEIGHT - 1); bool mouse_is_offscreen = x != scaled_x || y != scaled_y; if (mouse_is_offscreen != mouse_was_offscreen) { SDL_ShowCursor(mouse_is_offscreen); mouse_was_offscreen = mouse_is_offscreen; } - risc_mouse_moved(risc, x, RISC_SCREEN_HEIGHT - y - 1); + risc_mouse_moved(risc, x, RISC_FRAMEBUFFER_HEIGHT - y - 1); break; } @@ -191,7 +192,7 @@ int main (int argc, char *argv[]) { risc_set_time(risc, frame_start); risc_run(risc, CPU_HZ / FPS); - update_texture(risc_get_framebuffer_ptr(risc), texture); + update_texture(risc, texture); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, &display_rect); SDL_RenderPresent(renderer); @@ -215,18 +216,18 @@ static int clamp(int x, int min, int max) { static double scale_display(SDL_Window *window, SDL_Rect *rect) { int win_w, win_h; SDL_GetWindowSize(window, &win_w, &win_h); - double oberon_aspect = (double)RISC_SCREEN_WIDTH / RISC_SCREEN_HEIGHT; + double oberon_aspect = (double)RISC_FRAMEBUFFER_WIDTH / RISC_FRAMEBUFFER_HEIGHT; double window_aspect = (double)win_w / win_h; double scale; if (oberon_aspect > window_aspect) { - scale = (double)win_w / RISC_SCREEN_WIDTH; + scale = (double)win_w / RISC_FRAMEBUFFER_WIDTH; } else { - scale = (double)win_h / RISC_SCREEN_HEIGHT; + scale = (double)win_h / RISC_FRAMEBUFFER_HEIGHT; } - int w = (int)ceil(RISC_SCREEN_WIDTH * scale); - int h = (int)ceil(RISC_SCREEN_HEIGHT * scale); + int w = (int)ceil(RISC_FRAMEBUFFER_WIDTH * scale); + int h = (int)ceil(RISC_FRAMEBUFFER_HEIGHT * scale); *rect = (SDL_Rect){ .w = w, .h = h, .x = (win_w - w) / 2, @@ -235,53 +236,31 @@ static double scale_display(SDL_Window *window, SDL_Rect *rect) { return scale; } -static uint32_t cache[RISC_SCREEN_WIDTH * RISC_SCREEN_HEIGHT / 32]; -static uint32_t buffer[RISC_SCREEN_WIDTH * RISC_SCREEN_HEIGHT]; +static void update_texture(struct RISC *risc, SDL_Texture *texture) { + struct Damage damage = risc_get_framebuffer_damage(risc); + if (damage.y1 <= damage.y2) { + uint32_t out[RISC_FRAMEBUFFER_WIDTH * RISC_FRAMEBUFFER_HEIGHT]; + uint32_t *in = risc_get_framebuffer_ptr(risc); + uint32_t out_idx = 0; -static void init_texture(SDL_Texture *texture) { - memset(cache, 0, sizeof(cache)); - for (size_t i = 0; i < sizeof(buffer)/sizeof(buffer[0]); ++i) { - buffer[i] = BLACK; - } - SDL_UpdateTexture(texture, NULL, buffer, RISC_SCREEN_WIDTH * 4); -} - -static void update_texture(uint32_t *framebuffer, SDL_Texture *texture) { - // TODO: move dirty rectangle tracking into emulator core? - int dirty_y1 = RISC_SCREEN_HEIGHT; - int dirty_y2 = 0; - int dirty_x1 = RISC_SCREEN_WIDTH / 32; - int dirty_x2 = 0; - - int idx = 0; - for (int line = RISC_SCREEN_HEIGHT - 1; line >= 0; line--) { - for (int col = 0; col < RISC_SCREEN_WIDTH / 32; col++) { - uint32_t pixels = framebuffer[idx]; - if (pixels != cache[idx]) { - cache[idx] = pixels; - if (line < dirty_y1) dirty_y1 = line; - if (line > dirty_y2) dirty_y2 = line; - if (col < dirty_x1) dirty_x1 = col; - if (col > dirty_x2) dirty_x2 = col; - - uint32_t *buf_ptr = &buffer[line * RISC_SCREEN_WIDTH + col * 32]; + for (int line = damage.y2; line >= damage.y1; line--) { + int line_start = line * (RISC_FRAMEBUFFER_WIDTH / 32); + for (int col = damage.x1; col <= damage.x2; col++) { + uint32_t pixels = in[line_start + col]; for (int b = 0; b < 32; b++) { - *buf_ptr++ = (pixels & 1) ? WHITE : BLACK; + out[out_idx] = (pixels & 1) ? WHITE : BLACK; pixels >>= 1; + out_idx++; } } - ++idx; } - } - if (dirty_y1 <= dirty_y2) { SDL_Rect rect = { - .x = dirty_x1 * 32, - .y = dirty_y1, - .w = (dirty_x2 - dirty_x1 + 1) * 32, - .h = dirty_y2 - dirty_y1 + 1, + .x = damage.x1 * 32, + .y = RISC_FRAMEBUFFER_HEIGHT - damage.y2 - 1, + .w = (damage.x2 - damage.x1 + 1) * 32, + .h = (damage.y2 - damage.y1 + 1) }; - void *ptr = &buffer[dirty_y1 * RISC_SCREEN_WIDTH + dirty_x1 * 32]; - SDL_UpdateTexture(texture, &rect, ptr, RISC_SCREEN_WIDTH * 4); + SDL_UpdateTexture(texture, &rect, out, rect.w * 4); } }