Rewrite window code without macros

This commit is contained in:
David Guillen Fandos 2023-07-14 22:39:47 +02:00
parent 97435283d2
commit b88f0c0135
2 changed files with 224 additions and 333 deletions

View File

@ -171,7 +171,10 @@ typedef enum
REG_HALTCNT = 0x180 REG_HALTCNT = 0x180
} hardware_register; } hardware_register;
// Some useful macros to avoid reg math
#define REG_BGxCNT(n) (REG_BG0CNT + (n)) #define REG_BGxCNT(n) (REG_BG0CNT + (n))
#define REG_WINxH(n) (REG_WIN0H + (n))
#define REG_WINxV(n) (REG_WIN0V + (n))
#define FLASH_DEVICE_UNDEFINED 0x00 #define FLASH_DEVICE_UNDEFINED 0x00
#define FLASH_DEVICE_MACRONIX_64KB 0x1C #define FLASH_DEVICE_MACRONIX_64KB 0x1C

554
video.cc
View File

@ -3341,6 +3341,7 @@ static void render_scanline_obj_##alpha_op##_##map_space(u32 priority, \
} \ } \
} \ } \
// There are actually used to render sprites to the scanline
render_scanline_obj_builder(transparent, normal, 1D, no_partial_alpha); render_scanline_obj_builder(transparent, normal, 1D, no_partial_alpha);
render_scanline_obj_builder(transparent, normal, 2D, no_partial_alpha); render_scanline_obj_builder(transparent, normal, 2D, no_partial_alpha);
render_scanline_obj_builder(transparent, color16, 1D, no_partial_alpha); render_scanline_obj_builder(transparent, color16, 1D, no_partial_alpha);
@ -3351,6 +3352,8 @@ render_scanline_obj_builder(transparent, alpha_obj, 1D, no_partial_alpha);
render_scanline_obj_builder(transparent, alpha_obj, 2D, no_partial_alpha); render_scanline_obj_builder(transparent, alpha_obj, 2D, no_partial_alpha);
render_scanline_obj_builder(transparent, partial_alpha, 1D, partial_alpha); render_scanline_obj_builder(transparent, partial_alpha, 1D, partial_alpha);
render_scanline_obj_builder(transparent, partial_alpha, 2D, partial_alpha); render_scanline_obj_builder(transparent, partial_alpha, 2D, partial_alpha);
// These are used for winobj rendering
render_scanline_obj_builder(copy, copy_tile, 1D, no_partial_alpha); render_scanline_obj_builder(copy, copy_tile, 1D, no_partial_alpha);
render_scanline_obj_builder(copy, copy_tile, 2D, no_partial_alpha); render_scanline_obj_builder(copy, copy_tile, 2D, no_partial_alpha);
render_scanline_obj_builder(copy, copy_bitmap, 1D, no_partial_alpha); render_scanline_obj_builder(copy, copy_bitmap, 1D, no_partial_alpha);
@ -3991,40 +3994,38 @@ static void expand_brighten_partial_alpha(u32 *screen_src_ptr, u16 *screen_dest_
// Renders an entire scanline from 0 to 240, based on current color mode. // Renders an entire scanline from 0 to 240, based on current color mode.
template<bool tiled>
static void render_scanline_tile(u16 *scanline, u32 dispcnt) static void render_scanline(u16 *scanline, u32 dispcnt)
{ {
u32 current_layer; u32 current_layer;
u32 layer_order_pos; u32 layer_order_pos;
u32 bldcnt = read_ioreg(REG_BLDCNT);
render_scanline_layer_functions_tile();
render_layers_color_effect(render_layers, layer_count, if (tiled) {
render_condition_alpha, render_condition_fade, 0, 240); u32 bldcnt = read_ioreg(REG_BLDCNT);
} render_scanline_layer_functions_tile();
static void render_scanline_bitmap(u16 *scanline, u32 dispcnt) render_layers_color_effect(render_layers, layer_count,
{ render_condition_alpha, render_condition_fade, 0, 240);
render_scanline_layer_functions_bitmap(); } else {
u32 current_layer; render_scanline_layer_functions_bitmap();
u32 layer_order_pos; fill_line_bg(normal, scanline, 0, 240);
fill_line_bg(normal, scanline, 0, 240); for(layer_order_pos = 0; layer_order_pos < layer_count; layer_order_pos++)
for(layer_order_pos = 0; layer_order_pos < layer_count; layer_order_pos++)
{
current_layer = layer_order[layer_order_pos];
if(current_layer & 0x04)
{ {
render_obj_layer(normal, scanline, 0, 240); current_layer = layer_order[layer_order_pos];
} if(current_layer & 0x04)
else {
{ render_obj_layer(normal, scanline, 0, 240);
layer_renderers->normal_render(0, 240, scanline); }
else
{
layer_renderers->normal_render(0, 240, scanline);
}
} }
} }
} }
// Render layers from start to end based on if they're allowed in the // Render layers from start to end based on if they're allowed in the
// enable flags. // enable flags.
@ -4168,308 +4169,208 @@ static void render_scanline_conditional_bitmap(u32 start, u32 end, u16 *scanline
} }
#define window_x_coords(window_number) \ // If the window Y coordinates are out of the window range we can skip
window_##window_number##_x1 = \ // rendering the inside of the window.
read_ioreg(REG_WIN##window_number##H) >> 8; \ inline bool in_window_y(u32 vcount, u32 top, u32 bottom) {
window_##window_number##_x2 = \ // TODO: check if these are reversed when top-bottom are also reversed.
read_ioreg(REG_WIN##window_number##H) & 0xFF; \ if (top > 227) // This causes the window to be invisible
window_##window_number##_enable = \ return false;
(winin >> (window_number * 8)) & 0x3F; \ if (bottom > 227) // This makes it all visible
\ return true;
if(window_##window_number##_x1 > 240) \
window_##window_number##_x1 = 240; \
\
if(window_##window_number##_x2 > 240) \
window_##window_number##_x2 = 240 \
#define window_coords(window_number) \ if (top > bottom) /* Reversed: if not in the "band" */
u32 window_##window_number##_x1, window_##window_number##_x2; \ return vcount > top || vcount <= bottom;
u32 window_##window_number##_y1, window_##window_number##_y2; \
u32 window_##window_number##_enable = 0; \
window_##window_number##_y1 = \
read_ioreg(REG_WIN##window_number##V) >> 8; \
window_##window_number##_y2 = \
read_ioreg(REG_WIN##window_number##V) & 0xFF; \
\
if(window_##window_number##_y1 > window_##window_number##_y2) \
{ \
if((((vcount <= window_##window_number##_y2) || \
(vcount > window_##window_number##_y1)) || \
(window_##window_number##_y2 > 227)) && \
(window_##window_number##_y1 <= 227)) \
{ \
window_x_coords(window_number); \
} \
else \
{ \
window_##window_number##_x1 = 240; \
window_##window_number##_x2 = 240; \
} \
} \
else \
{ \
if((((vcount >= window_##window_number##_y1) && \
(vcount < window_##window_number##_y2)) || \
(window_##window_number##_y2 > 227)) && \
(window_##window_number##_y1 <= 227)) \
{ \
window_x_coords(window_number); \
} \
else \
{ \
window_##window_number##_x1 = 240; \
window_##window_number##_x2 = 240; \
} \
} \
#define render_window_segment(type, start, end, window_type) \ return vcount >= top && vcount < bottom;
if(start != end) \ }
{ \
render_scanline_conditional_##type(start, end, scanline, \
window_##window_type##_enable, dispcnt, bldcnt, layer_renderers); \
} \
#define render_window_segment_unequal(type, start, end, window_type) \ // Temporary wrap functions, to be removed once all the plain calls do not exist
render_scanline_conditional_##type(start, end, scanline, \
window_##window_type##_enable, dispcnt, bldcnt, layer_renderers) \
#define render_window_segment_clip(type, clip_start, clip_end, start, end, \ template <bool tiled>
window_type) \ static inline void render_scanline_conditional(u32 start, u32 end,
{ \ u16 *scanline, u32 enable_flags, u32 dispcnt, u32 bldcnt)
if(start != end) \ {
{ \ if (tiled) {
if(start < clip_start) \ const tile_layer_render_struct *layer_renderers = tile_mode_renderers[dispcnt & 0x07];
{ \ render_scanline_conditional_tile(start, end, scanline, enable_flags, dispcnt, bldcnt, layer_renderers);
if(end > clip_start) \ }
{ \ else {
if(end > clip_end) \ const bitmap_layer_render_struct *layer_renderers = &bitmap_mode_renderers[(dispcnt & 0x07) - 3];
{ \ render_scanline_conditional_bitmap(start, end, scanline, enable_flags, dispcnt, bldcnt, layer_renderers);
render_window_segment_unequal(type, clip_start, clip_end, \ }
window_type); \ }
} \
else \
{ \
render_window_segment_unequal(type, clip_start, end, window_type); \
} \
} \
} \
else \
\
if(end > clip_end) \
{ \
if(start < clip_end) \
render_window_segment_unequal(type, start, clip_end, window_type); \
} \
else \
{ \
render_window_segment_unequal(type, start, end, window_type); \
} \
} \
} \
#define render_window_clip_1(type, start, end) \ // Renders window1 (low priority window) and outside/obj areas for a given range.
if(window_1_x1 != 240) \ template <bool tiled>
{ \ static void render_windowobj_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end)
if(window_1_x1 > window_1_x2) \ {
{ \ u32 bldcnt = read_ioreg(REG_BLDCNT);
render_window_segment_clip(type, start, end, 0, window_1_x2, 1); \ u32 winout = read_ioreg(REG_WINOUT);
render_window_segment_clip(type, start, end, window_1_x2, window_1_x1, \ u32 wndout_enable = winout & 0x3F;
out); \ render_scanline_conditional<tiled>(start, end, scanline,
render_window_segment_clip(type, start, end, window_1_x1, 240, 1); \ wndout_enable, dispcnt, bldcnt);
} \
else \
{ \
render_window_segment_clip(type, start, end, 0, window_1_x1, out); \
render_window_segment_clip(type, start, end, window_1_x1, window_1_x2, \
1); \
render_window_segment_clip(type, start, end, window_1_x2, 240, out); \
} \
} \
else \
{ \
render_window_segment(type, start, end, out); \
} \
#define render_window_clip_obj(type, start, end); \ if (dispcnt >> 15) {
render_window_segment(type, start, end, out); \ // Perform the actual object rendering in copy mode
if(dispcnt & 0x40) \ if (tiled) {
render_scanline_obj_copy_##type##_1D(4, start, end, scanline); \ if (dispcnt & 0x40)
else \ render_scanline_obj_copy_tile_1D(4, start, end, scanline);
render_scanline_obj_copy_##type##_2D(4, start, end, scanline) \ else
render_scanline_obj_copy_tile_2D(4, start, end, scanline);
} else {
if (dispcnt & 0x40)
render_scanline_obj_copy_bitmap_1D(4, start, end, scanline);
else
render_scanline_obj_copy_bitmap_2D(4, start, end, scanline);
}
}
}
// Renders window1 (low priority window) and outside/obj areas for a given range.
template <bool tiled>
static void render_window1_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end)
{
u32 bldcnt = read_ioreg(REG_BLDCNT);
u32 winout = read_ioreg(REG_WINOUT);
u32 wndout_enable = winout & 0x3F;
switch (dispcnt >> 14) {
case 0x0: // No Win1 nor WinObj
render_scanline_conditional<tiled>(
start, end, scanline, wndout_enable, dispcnt, bldcnt);
break;
case 0x2: // Only winobj enabled, render it.
render_windowobj_pass<tiled>(scanline, dispcnt, start, end);
break;
case 0x1: case 0x3: // Win1 is enabled (and perhaps WinObj too)
{
// Attempt to render window 1
u32 vcount = read_ioreg(REG_VCOUNT);
// Check the Y coordinates to check if they fall in the right row
u32 win_top = read_ioreg(REG_WINxV(1)) >> 8;
u32 win_bot = read_ioreg(REG_WINxV(1)) & 0xFF;
// Check the X coordinates and generate up to three segments
// Clip the coordinates to the [start, end) range.
u32 win_l = MAX(start, MIN(end, read_ioreg(REG_WINxH(1)) >> 8));
u32 win_r = MAX(start, MIN(end, read_ioreg(REG_WINxH(1)) & 0xFF));
if (!in_window_y(vcount, win_top, win_bot) || (win_l == win_r))
// Window1 is completely out, just render all out.
render_windowobj_pass<tiled>(
scanline, dispcnt, start, end);
else {
// Render win1 withtin the clipped range
// Enable bits for stuff inside the window (and outside)
u32 winin = read_ioreg(REG_WININ);
u32 wnd1_enable = (winin >> 8) & 0x3F;
// If the window is defined upside down, the areas are inverted.
if (win_l < win_r) {
// Render [start, win_l) range (which is outside the window)
if (win_l != start)
render_windowobj_pass<tiled>(scanline, dispcnt, start, win_l);
// Render the actual window0 pixels
render_scanline_conditional<tiled>(
win_l, win_r, scanline, wnd1_enable, dispcnt, bldcnt);
// Render the [win_l, end] range (outside)
if (win_r != end)
render_windowobj_pass<tiled>(scanline, dispcnt, win_r, end);
} else {
// Render [0, win_r) range (which is "inside" window0)
if (win_r != start)
render_scanline_conditional<tiled>(
start, win_r, scanline, wnd1_enable, dispcnt, bldcnt);
// The actual window is now outside, render recursively
render_windowobj_pass<tiled>(scanline, dispcnt, win_r, win_l);
// Render the [win_l, 240] range ("inside")
if (win_l != end)
render_scanline_conditional<tiled>(
win_l, end, scanline, wnd1_enable, dispcnt, bldcnt);
}
}
}
break;
};
}
// Renders window0 (high priority window) and renders window1 or out
// on the area that falls outside. It will call the above function for
// outside areas to "recursively" render segments.
template <bool tiled>
static void render_window0_pass(u16 *scanline, u16 dispcnt)
{
u32 bldcnt = read_ioreg(REG_BLDCNT);
u32 vcount = read_ioreg(REG_VCOUNT);
// Check the Y coordinates to check if they fall in the right row
u32 win_top = read_ioreg(REG_WINxV(0)) >> 8;
u32 win_bot = read_ioreg(REG_WINxV(0)) & 0xFF;
// Check the X coordinates and generate up to three segments
u32 win_l = MIN(240, read_ioreg(REG_WINxH(0)) >> 8);
u32 win_r = MIN(240, read_ioreg(REG_WINxH(0)) & 0xFF);
if (!in_window_y(vcount, win_top, win_bot) || (win_l == win_r))
// No windowing, everything is "outside", just render win1.
render_window1_pass<tiled>(scanline, dispcnt, 0, 240);
else {
u32 winin = read_ioreg(REG_WININ);
// Enable bits for stuff inside the window
u32 wnd0_enable = (winin) & 0x3F;
// If the window is defined upside down, the areas are inverted.
if (win_l < win_r) {
// Render [0, win_l) range (which is outside the window)
if (win_l)
render_window1_pass<tiled>(scanline, dispcnt, 0, win_l);
// Render the actual window0 pixels
render_scanline_conditional<tiled>(
win_l, win_r, scanline, wnd0_enable, dispcnt, bldcnt);
// Render the [win_l, 240] range (outside)
if (win_r != 240)
render_window1_pass<tiled>(scanline, dispcnt, win_r, 240);
} else {
// Render [0, win_r) range (which is "inside" window0)
if (win_r)
render_scanline_conditional<tiled>(
0, win_r, scanline, wnd0_enable, dispcnt, bldcnt);
// The actual window is now outside, render recursively
render_window1_pass<tiled>(scanline, dispcnt, win_r, win_l);
// Render the [win_l, 240] range ("inside")
if (win_l != 240)
render_scanline_conditional<tiled>(
win_l, 240, scanline, wnd0_enable, dispcnt, bldcnt);
}
}
}
#define render_window_segment_clip_obj(type, clip_start, clip_end, start, \ // Renders a full scaleline, taking into consideration windowing effects.
end) \ // Breaks the rendering step into N steps, for each windowed region.
{ \ template <bool tiled>
if(start != end) \ static void render_scanline_window(u16 *scanline, u16 dispcnt)
{ \ {
if(start < clip_start) \ u32 win_ctrl = (dispcnt >> 13);
{ \
if(end > clip_start) \
{ \
if(end > clip_end) \
{ \
render_window_clip_obj(type, clip_start, clip_end); \
} \
else \
{ \
render_window_clip_obj(type, clip_start, end); \
} \
} \
} \
else \
\
if(end > clip_end) \
{ \
if(start < clip_end) \
{ \
render_window_clip_obj(type, start, clip_end); \
} \
} \
else \
{ \
render_window_clip_obj(type, start, end); \
} \
} \
} \
// Priority decoding for windows
#define render_window_clip_1_obj(type, start, end) \ switch (win_ctrl) {
if(window_1_x1 != 240) \ case 0x1: case 0x3: case 0x5: case 0x7:
{ \ // Window 0 is enabled, call the win0 render function. It does recursively
if(window_1_x1 > window_1_x2) \ // check for window 1 and Obj, so no worries.
{ \ render_window0_pass<tiled>(scanline, dispcnt);
render_window_segment_clip(type, start, end, 0, window_1_x2, 1); \ break;
render_window_segment_clip_obj(type, start, end, window_1_x2, \ case 0x2: case 0x6:
window_1_x1); \ // Window 1 is active, call the window1 renderer.
render_window_segment_clip(type, start, end, window_1_x1, 240, 1); \ render_window1_pass<tiled>(scanline, dispcnt, 0, 240);
} \ break;
else \ case 0x4:
{ \ // Only winobj seems active
render_window_segment_clip_obj(type, start, end, 0, window_1_x1); \ render_windowobj_pass<tiled>(scanline, dispcnt, 0, 240);
render_window_segment_clip(type, start, end, window_1_x1, window_1_x2, \ break;
1); \ case 0x0:
render_window_segment_clip_obj(type, start, end, window_1_x2, 240); \ // No windows are active?
} \ render_scanline<tiled>(scanline, dispcnt);
} \ break;
else \ }
{ \ }
render_window_clip_obj(type, start, end); \
} \
#define render_window_single(type, window_number) \
u32 winin = read_ioreg(REG_WININ); \
window_coords(window_number); \
if(window_##window_number##_x1 > window_##window_number##_x2) \
{ \
render_window_segment(type, 0, window_##window_number##_x2, \
window_number); \
render_window_segment(type, window_##window_number##_x2, \
window_##window_number##_x1, out); \
render_window_segment(type, window_##window_number##_x1, 240, \
window_number); \
} \
else \
{ \
render_window_segment(type, 0, window_##window_number##_x1, out); \
render_window_segment(type, window_##window_number##_x1, \
window_##window_number##_x2, window_number); \
render_window_segment(type, window_##window_number##_x2, 240, out); \
} \
#define render_window_multi(type, front, back) \
if(window_##front##_x1 > window_##front##_x2) \
{ \
render_window_segment(type, 0, window_##front##_x2, front); \
render_window_clip_##back(type, window_##front##_x2, \
window_##front##_x1); \
render_window_segment(type, window_##front##_x1, 240, front); \
} \
else \
{ \
render_window_clip_##back(type, 0, window_##front##_x1); \
render_window_segment(type, window_##front##_x1, window_##front##_x2, \
front); \
render_window_clip_##back(type, window_##front##_x2, 240); \
} \
#define render_scanline_window_builder(type) \
static void render_scanline_window_##type(u16 *scanline, u32 dispcnt) \
{ \
u32 vcount = read_ioreg(REG_VCOUNT); \
u32 winout = read_ioreg(REG_WINOUT); \
u32 bldcnt = read_ioreg(REG_BLDCNT); \
u32 window_out_enable = winout & 0x3F; \
\
render_scanline_layer_functions_##type(); \
\
switch(dispcnt >> 13) \
{ \
/* Just window 0 */ \
case 0x01: \
{ \
render_window_single(type, 0); \
break; \
} \
\
/* Just window 1 */ \
case 0x02: \
{ \
render_window_single(type, 1); \
break; \
} \
\
/* Windows 1 and 2 */ \
case 0x03: \
{ \
u32 winin = read_ioreg(REG_WININ); \
window_coords(0); \
window_coords(1); \
render_window_multi(type, 0, 1); \
break; \
} \
\
/* Just OBJ windows */ \
case 0x04: \
{ \
render_window_clip_obj(type, 0, 240); \
break; \
} \
\
/* Window 0 and OBJ window */ \
case 0x05: \
{ \
u32 winin = read_ioreg(REG_WININ); \
window_coords(0); \
render_window_multi(type, 0, obj); \
break; \
} \
\
/* Window 1 and OBJ window */ \
case 0x06: \
{ \
u32 winin = read_ioreg(REG_WININ); \
window_coords(1); \
render_window_multi(type, 1, obj); \
break; \
} \
\
/* Window 0, 1, and OBJ window */ \
case 0x07: \
{ \
u32 winin = read_ioreg(REG_WININ); \
window_coords(0); \
window_coords(1); \
render_window_multi(type, 0, 1_obj); \
break; \
} \
} \
} \
render_scanline_window_builder(tile);
render_scanline_window_builder(bitmap);
static const u8 active_layers[] = { static const u8 active_layers[] = {
0x1F, // Mode 0, Tile BG0-3 and OBJ 0x1F, // Mode 0, Tile BG0-3 and OBJ
@ -4485,7 +4386,7 @@ static const u8 active_layers[] = {
void update_scanline(void) void update_scanline(void)
{ {
u32 pitch = get_screen_pitch(); u32 pitch = get_screen_pitch();
u32 dispcnt = read_ioreg(REG_DISPCNT); u16 dispcnt = read_ioreg(REG_DISPCNT);
u32 vcount = read_ioreg(REG_VCOUNT); u32 vcount = read_ioreg(REG_VCOUNT);
u16 *screen_offset = get_screen_pixels() + (vcount * pitch); u16 *screen_offset = get_screen_pixels() + (vcount * pitch);
u32 video_mode = dispcnt & 0x07; u32 video_mode = dispcnt & 0x07;
@ -4510,24 +4411,11 @@ void update_scanline(void)
} }
else else
{ {
// Modes 0..2 are tiled modes, 3..5 are bitmap-based modes.
if(video_mode < 3) if(video_mode < 3)
{ render_scanline_window<true>(screen_offset, dispcnt);
if(dispcnt >> 13)
{
render_scanline_window_tile(screen_offset, dispcnt);
}
else
{
render_scanline_tile(screen_offset, dispcnt);
}
}
else else
{ render_scanline_window<false>(screen_offset, dispcnt);
if(dispcnt >> 13)
render_scanline_window_bitmap(screen_offset, dispcnt);
else
render_scanline_bitmap(screen_offset, dispcnt);
}
} }
affine_reference_x[0] += (s16)read_ioreg(REG_BG2PB); affine_reference_x[0] += (s16)read_ioreg(REG_BG2PB);