comparison vdp.c @ 1881:55198fc9cc1f

Don't render lines that are cropped by overscan. Allows submitting frame earlier when bottom overscan is non-zero which can reduce latency in some cases
author Mike Pavone <pavone@retrodev.com>
date Mon, 16 Sep 2019 00:45:48 -0700
parents 881083d76212
children b5549258b98b
comparison
equal deleted inserted replaced
1880:e77f7a7c79a5 1881:55198fc9cc1f
68 {127, 0, 0}, //Window 68 {127, 0, 0}, //Window
69 {0, 127, 0}, //B 69 {0, 127, 0}, //B
70 {127, 0, 127} //Sprites 70 {127, 0, 127} //Sprites
71 }; 71 };
72 72
73 static uint32_t calc_crop(uint32_t crop, uint32_t border)
74 {
75 return crop >= border ? 0 : border - crop;
76 }
77
73 static void update_video_params(vdp_context *context) 78 static void update_video_params(vdp_context *context)
74 { 79 {
80 uint32_t top_crop = render_overscan_top();
81 uint32_t bot_crop = render_overscan_bot();
82 uint32_t border_top;
75 if (context->regs[REG_MODE_2] & BIT_MODE_5) { 83 if (context->regs[REG_MODE_2] & BIT_MODE_5) {
76 if (context->regs[REG_MODE_2] & BIT_PAL) { 84 if (context->regs[REG_MODE_2] & BIT_PAL) {
77 if (context->flags2 & FLAG2_REGION_PAL) { 85 if (context->flags2 & FLAG2_REGION_PAL) {
78 context->inactive_start = PAL_INACTIVE_START; 86 context->inactive_start = PAL_INACTIVE_START;
79 context->border_top = BORDER_TOP_V30_PAL; 87 border_top = BORDER_TOP_V30_PAL;
80 context->border_bot = BORDER_BOT_V30_PAL; 88 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V30_PAL);
81 } else { 89 } else {
82 //the behavior here is rather weird and needs more investigation 90 //the behavior here is rather weird and needs more investigation
83 context->inactive_start = 0xF0; 91 context->inactive_start = 0xF0;
84 context->border_top = 1; 92 border_top = 1;
85 context->border_bot = 3; 93 context->border_bot = calc_crop(bot_crop, 3);
86 } 94 }
87 } else { 95 } else {
88 context->inactive_start = NTSC_INACTIVE_START; 96 context->inactive_start = NTSC_INACTIVE_START;
89 if (context->flags2 & FLAG2_REGION_PAL) { 97 if (context->flags2 & FLAG2_REGION_PAL) {
90 context->border_top = BORDER_TOP_V28_PAL; 98 border_top = BORDER_TOP_V28_PAL;
91 context->border_bot = BORDER_BOT_V28_PAL; 99 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V28_PAL);
92 } else { 100 } else {
93 context->border_top = BORDER_TOP_V28; 101 border_top = BORDER_TOP_V28;
94 context->border_bot = BORDER_TOP_V28; 102 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V28);
95 } 103 }
96 } 104 }
97 if (context->regs[REG_MODE_4] & BIT_H40) { 105 if (context->regs[REG_MODE_4] & BIT_H40) {
98 context->max_sprites_frame = MAX_SPRITES_FRAME; 106 context->max_sprites_frame = MAX_SPRITES_FRAME;
99 context->max_sprites_line = MAX_SPRITES_LINE; 107 context->max_sprites_line = MAX_SPRITES_LINE;
110 } 118 }
111 } 119 }
112 } else { 120 } else {
113 context->inactive_start = MODE4_INACTIVE_START; 121 context->inactive_start = MODE4_INACTIVE_START;
114 if (context->flags2 & FLAG2_REGION_PAL) { 122 if (context->flags2 & FLAG2_REGION_PAL) {
115 context->border_top = BORDER_TOP_V24_PAL; 123 border_top = BORDER_TOP_V24_PAL;
116 context->border_bot = BORDER_BOT_V24_PAL; 124 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V24_PAL);
117 } else { 125 } else {
118 context->border_top = BORDER_TOP_V24; 126 border_top = BORDER_TOP_V24;
119 context->border_bot = BORDER_BOT_V24; 127 context->border_bot = calc_crop(bot_crop, BORDER_BOT_V24);
120 } 128 }
121 if (!(context->regs[REG_MODE_1] & BIT_MODE_4)){ 129 if (!(context->regs[REG_MODE_1] & BIT_MODE_4)){
122 context->state = INACTIVE; 130 context->state = INACTIVE;
123 } else if (context->state == INACTIVE) { 131 } else if (context->state == INACTIVE) {
124 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active 132 //Undo forced INACTIVE state due to neither Mode 4 nor Mode 5 being active
128 else if (context->vcounter == 0x1FF) { 136 else if (context->vcounter == 0x1FF) {
129 context->state = PREPARING; 137 context->state = PREPARING;
130 } 138 }
131 } 139 }
132 } 140 }
141 context->border_top = calc_crop(top_crop, border_top);
142 context->top_offset = border_top - context->border_top;
133 } 143 }
134 144
135 static uint8_t color_map_init_done; 145 static uint8_t color_map_init_done;
136 146
137 vdp_context *init_vdp_context(uint8_t region_pal) 147 vdp_context *init_vdp_context(uint8_t region_pal)
138 { 148 {
139 vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE); 149 vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE);
140 if (headless) { 150 if (headless) {
141 context->output = malloc(LINEBUF_SIZE * sizeof(uint32_t)); 151 context->fb = malloc(512 * LINEBUF_SIZE * sizeof(uint32_t));
142 context->output_pitch = 0; 152 context->output_pitch = LINEBUF_SIZE * sizeof(uint32_t);
143 } else { 153 } else {
144 context->cur_buffer = FRAMEBUFFER_ODD; 154 context->cur_buffer = FRAMEBUFFER_ODD;
145 context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch); 155 context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch);
146 } 156 }
147 context->sprite_draws = MAX_SPRITES_LINE; 157 context->sprite_draws = MAX_SPRITES_LINE;
233 } 243 }
234 if (region_pal) { 244 if (region_pal) {
235 context->flags2 |= FLAG2_REGION_PAL; 245 context->flags2 |= FLAG2_REGION_PAL;
236 } 246 }
237 update_video_params(context); 247 update_video_params(context);
238 if (!headless) { 248 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
239 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
240 }
241 return context; 249 return context;
242 } 250 }
243 251
244 void vdp_free(vdp_context *context) 252 void vdp_free(vdp_context *context)
245 { 253 {
2016 } 2024 }
2017 } 2025 }
2018 2026
2019 void vdp_force_update_framebuffer(vdp_context *context) 2027 void vdp_force_update_framebuffer(vdp_context *context)
2020 { 2028 {
2021 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) 2029 uint16_t lines_max = context->inactive_start + context->border_bot + context->border_top;
2022 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL
2023 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2024 2030
2025 uint16_t to_fill = lines_max - context->output_lines; 2031 uint16_t to_fill = lines_max - context->output_lines;
2026 memset( 2032 memset(
2027 ((char *)context->fb) + context->output_pitch * context->output_lines, 2033 ((char *)context->fb) + context->output_pitch * context->output_lines,
2028 0, 2034 0,
2033 vdp_update_per_frame_debug(context); 2039 vdp_update_per_frame_debug(context);
2034 } 2040 }
2035 2041
2036 static void advance_output_line(vdp_context *context) 2042 static void advance_output_line(vdp_context *context)
2037 { 2043 {
2038 if (headless) { 2044 uint16_t lines_max = context->inactive_start + context->border_bot + context->border_top;
2039 if (context->vcounter == context->inactive_start) { 2045
2040 context->frame++; 2046 if (context->output_lines == lines_max) {
2041 } 2047 if (!headless) {
2042 context->vcounter &= 0x1FF;
2043 } else {
2044 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL)
2045 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL
2046 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2047
2048 if (context->output_lines == lines_max) {
2049 render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER)); 2048 render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
2050 context->cur_buffer = context->flags2 & FLAG2_EVEN_FIELD ? FRAMEBUFFER_EVEN : FRAMEBUFFER_ODD; 2049 context->cur_buffer = context->flags2 & FLAG2_EVEN_FIELD ? FRAMEBUFFER_EVEN : FRAMEBUFFER_ODD;
2051 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch); 2050 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
2052 vdp_update_per_frame_debug(context); 2051 }
2053 context->h40_lines = 0; 2052 vdp_update_per_frame_debug(context);
2054 context->frame++; 2053 context->h40_lines = 0;
2054 context->frame++;
2055 context->output_lines = 0;
2056 }
2057 uint32_t output_line = context->vcounter;
2058 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) {
2059 //vcounter increment occurs much later in Mode 4
2060 output_line++;
2061 }
2062 if (output_line < context->inactive_start + context->border_bot && context->output_lines > 0) {
2063 output_line = context->output_lines++;//context->border_top + context->vcounter;
2064 } else if (output_line >= 0x200 - context->border_top || (!context->border_top && !output_line)) {
2065 if (output_line == 0x200 - context->border_top || (!context->border_top && !output_line)) {
2066 //We're at the top of the display, force context->output_lines to be zero to avoid
2067 //potential screen rolling if the mode is changed at an inopportune time
2055 context->output_lines = 0; 2068 context->output_lines = 0;
2056 } 2069 }
2057 uint32_t output_line = context->vcounter; 2070 output_line = context->output_lines++;//context->vcounter - (0x200 - context->border_top);
2058 if (!(context->regs[REG_MODE_2] & BIT_MODE_5)) { 2071 } else {
2059 //vcounter increment occurs much later in Mode 4 2072 context->output = NULL;
2060 output_line++; 2073 return;
2061 } 2074 }
2062 if (output_line < context->inactive_start + context->border_bot && context->output_lines > 0) { 2075 output_line += context->top_offset;
2063 output_line = context->output_lines++;//context->border_top + context->vcounter; 2076 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * output_line);
2064 } else if (output_line >= 0x200 - context->border_top) {
2065 if (output_line == 0x200 - context->border_top) {
2066 //We're at the top of the display, force context->output_lines to be zero to avoid
2067 //potential screen rolling if the mode is changed at an inopportune time
2068 context->output_lines = 0;
2069 }
2070 output_line = context->output_lines++;//context->vcounter - (0x200 - context->border_top);
2071 } else {
2072 context->output = NULL;
2073 }
2074 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * output_line);
2075 #ifdef DEBUG_FB_FILL 2077 #ifdef DEBUG_FB_FILL
2076 for (int i = 0; i < LINEBUF_SIZE; i++) 2078 for (int i = 0; i < LINEBUF_SIZE; i++)
2077 { 2079 {
2078 context->output[i] = 0xFFFF00FF; 2080 context->output[i] = 0xFFFF00FF;
2079 } 2081 }
2080 #endif 2082 #endif
2081 if (context->output && (context->regs[REG_MODE_4] & BIT_H40)) { 2083 if (context->output && (context->regs[REG_MODE_4] & BIT_H40)) {
2082 context->h40_lines++; 2084 context->h40_lines++;
2083 }
2084 } 2085 }
2085 } 2086 }
2086 2087
2087 void vdp_release_framebuffer(vdp_context *context) 2088 void vdp_release_framebuffer(vdp_context *context)
2088 { 2089 {
2091 } 2092 }
2092 2093
2093 void vdp_reacquire_framebuffer(vdp_context *context) 2094 void vdp_reacquire_framebuffer(vdp_context *context)
2094 { 2095 {
2095 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch); 2096 context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
2096 uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) 2097 uint16_t lines_max = context->inactive_start + context->border_bot + context->border_top;
2097 ? 240 + BORDER_TOP_V30_PAL + BORDER_BOT_V30_PAL
2098 : 224 + BORDER_TOP_V28 + BORDER_BOT_V28;
2099 if (context->output_lines <= lines_max && context->output_lines > 0) { 2098 if (context->output_lines <= lines_max && context->output_lines > 0) {
2100 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1)); 2099 context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1 + context->top_offset));
2101 } else { 2100 } else {
2102 context->output = NULL; 2101 context->output = NULL;
2103 } 2102 }
2104 } 2103 }
2105 2104