Simplify display damage tracking

This commit is contained in:
Peter De Wachter 2014-04-30 22:22:06 +02:00
parent dbe22cc15d
commit f702a425d2
3 changed files with 83 additions and 63 deletions

48
risc.c
View File

@ -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;
}

9
risc.h
View File

@ -5,11 +5,15 @@
#include <stdint.h>
#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

View File

@ -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);
}
}