diff --git a/debug.c b/debug.c index 30cc38a..7be1c38 100644 --- a/debug.c +++ b/debug.c @@ -1121,6 +1121,37 @@ debug_save_raw(struct debug_struct* p_debug, util_file_close(p_file); } +static void +debug_save_screen(struct debug_struct* p_debug, const char* p_file_name) { + struct util_file* p_file; + struct render_struct* p_render = p_debug->p_render; + uint32_t* p_buffer; + uint32_t size; + + if (!render_has_buffer(p_render)) { + (void) printf("no render buffer; start with -headless-render or " + "-frame-cycles\n"); + return; + } + + p_buffer = render_get_buffer(p_render); + size = render_get_buffer_size(p_render); + + p_file = util_file_try_open(p_file_name, 1, 1); + if (p_file == NULL) { + log_do_log(k_log_misc, k_log_warning, "cannot open file %s", p_file_name); + return; + } + + util_file_write(p_file, p_buffer, size); + util_file_close(p_file); + (void) printf("saved %ux%u BGRA (%u bytes) to %s\n", + render_get_width(p_render), + render_get_height(p_render), + size, + p_file_name); +} + static void debug_print_registers(uint8_t reg_a, uint8_t reg_x, @@ -2357,6 +2388,8 @@ debug_callback_common(struct debug_struct* p_debug, (parse_hex_int3 >= 0) && (parse_hex_int3 < 65536)) { debug_save_raw(p_debug, p_param_1_str, parse_hex_int2, parse_hex_int3); + } else if (!strcmp(p_command, "savescreen") && (p_param_1_str != NULL)) { + debug_save_screen(p_debug, p_param_1_str); } else if (!strcmp(p_command, "ss")) { state_save(p_bbc, p_param_1_str); } else if (!strcmp(p_command, "d")) { @@ -2526,6 +2559,7 @@ debug_callback_common(struct debug_struct* p_debug, "find ... : find a byte sequence, starting at , length \n" "loadmem : load memory to from raw file \n" "savemem : save memory from , length to raw file \n" + "savescreen : save render buffer to raw BGRA file \n" "sys : show system VIA registers\n" "user : show user VIA registers\n" "r : show regular registers\n" diff --git a/main.c b/main.c index 9f36527..9cf4033 100644 --- a/main.c +++ b/main.c @@ -113,6 +113,7 @@ beebjit_main(void) { int disc_mutable_flag = 0; int terminal_flag = 0; int headless_flag = 0; + int headless_render_flag = 0; int fasttape_flag = 0; int convert_hfe_flag = 0; int convert_ssd_flag = 0; @@ -307,6 +308,8 @@ beebjit_main(void) { terminal_flag = 1; } else if (!strcmp(arg, "-headless")) { headless_flag = 1; + } else if (!strcmp(arg, "-headless-render")) { + headless_render_flag = 1; } else if (!strcmp(arg, "-fasttape")) { fasttape_flag = 1; } else if (!strcmp(arg, "-convert-hfe")) { @@ -394,6 +397,7 @@ beebjit_main(void) { "-max-frames : max frame images to save, default 1.\n" "-exit-on-max-frames: exit the process once max-frames is hit.\n" "-frames-dir : directory for frame files, default '.'.\n" +"-headless-render : in -headless mode, allocate a render buffer.\n" "-watford : for a model B with a 1770, load Watford DDFS ROM.\n" "-opus : for a model B with a 1770, load Opus DDOS ROM.\n" "-dfs12 : for a model B with an 8271, load newer DFS v1.2 ROM.\n" @@ -594,7 +598,7 @@ beebjit_main(void) { render_set_buffer(p_render, p_render_buffer); window_handle = os_window_get_handle(p_window); - } else if (frame_cycles > 0) { + } else if ((frame_cycles > 0) || headless_render_flag) { /* TODO: push this down into video.c. */ render_create_internal_buffer(p_render); } diff --git a/run_functional_tests.sh b/run_functional_tests.sh index 7e8935f..3c87deb 100755 --- a/run_functional_tests.sh +++ b/run_functional_tests.sh @@ -52,6 +52,38 @@ echo 'Checking RVI rendering.' -opt video:always-render \ -commands "breakat 11000000;c;eval '(frame_buffer_crc32==0x2c23c1b6)||bail';q" +echo 'Checking -headless-render produces matching framebuffer.' +# This re-runs the RVI rendering scenario in headless mode with +# -headless-render. The CRC must match the GUI-mode value above, proving +# that headless rendering is pixel-identical to GUI rendering and that the +# buffer allocation path triggered by -headless-render works. +./beebjit -0 test/display/raster-c.ssd \ + -mode jit \ + -autoboot \ + -fast -accurate -debug \ + -headless -headless-render \ + -opt video:always-render \ + -commands "breakat 11000000;c;eval '(frame_buffer_crc32==0x2c23c1b6)||bail';q" + +echo 'Checking savescreen dumps render buffer to file.' +# This runs the same scenario and uses savescreen to dump the BGRA buffer. +# The file size must equal width * height * 4 = 768 * 640 * 4 = 1966080 +# bytes for the current render dimensions. +TMP_BGRA=$(mktemp) +./beebjit -0 test/display/raster-c.ssd \ + -mode jit \ + -autoboot \ + -fast -accurate -debug \ + -headless -headless-render \ + -opt video:always-render \ + -commands "breakat 11000000;c;savescreen $TMP_BGRA;q" > /dev/null +SAVESCREEN_SIZE=$(wc -c < "$TMP_BGRA") +rm -f "$TMP_BGRA" +if [ "$SAVESCREEN_SIZE" -ne 1966080 ]; then + echo "savescreen file size $SAVESCREEN_SIZE != expected 1966080" + exit 1 +fi + # This checks the framebuffer looks as expected, in an RVI test case that uses # teletext output to implement pre-line blanking. ./beebjit -0 test/display/rvi-working.ssd \