Rewrite layer rendering logic for tiled modes.

Bitmap mode does not really support this yet, therefore color effects
are not being rendered.
This commit is contained in:
David Guillen Fandos 2023-07-25 23:36:56 +02:00
parent 7b3c092584
commit 543ba652ba
1 changed files with 289 additions and 388 deletions

669
video.cc
View File

@ -36,14 +36,8 @@ typedef void (* bitmap_render_function)(u32 start, u32 end, void *dest_ptr);
typedef struct typedef struct
{ {
tile_render_function normal_render_base; tile_render_function base[4];
tile_render_function normal_render_transparent; tile_render_function trans[4];
tile_render_function alpha_render_base;
tile_render_function alpha_render_transparent;
tile_render_function color16_render_base;
tile_render_function color16_render_transparent;
tile_render_function color32_render_base;
tile_render_function color32_render_transparent;
} tile_layer_render_struct; } tile_layer_render_struct;
typedef struct typedef struct
@ -54,10 +48,10 @@ typedef struct
} bitmap_layer_render_struct; } bitmap_layer_render_struct;
static void render_scanline_conditional_tile(u32 start, u32 end, u16 *scanline, static void render_scanline_conditional_tile(u32 start, u32 end, u16 *scanline,
u32 enable_flags, u32 dispcnt, u32 bldcnt, const tile_layer_render_struct u32 enable_flags, const tile_layer_render_struct
*layer_renderers); *layer_renderers);
static void render_scanline_conditional_bitmap(u32 start, u32 end, u16 *scanline, static void render_scanline_conditional_bitmap(u32 start, u32 end, u16 *scanline,
u32 enable_flags, u32 dispcnt, u32 bldcnt, const bitmap_layer_render_struct u32 enable_flags, const bitmap_layer_render_struct
*layer_renderers); *layer_renderers);
#define tile_expand_base_normal(index) \ #define tile_expand_base_normal(index) \
@ -1098,14 +1092,17 @@ static inline void render_scanline_bitmap(u32 start, u32 end, void *scanline) {
#define tile_layer_render_functions(type) \ #define tile_layer_render_functions(type) \
{ \ { \
{ \
render_scanline_##type<u16, FULLCOLOR, false>, \ render_scanline_##type<u16, FULLCOLOR, false>, \
render_scanline_##type<u16, FULLCOLOR, true>, \
render_scanline_##type<u32, STCKCOLOR, false>, /* for alpha blending */ \
render_scanline_##type<u32, STCKCOLOR, true>, \
render_scanline_##type<u16, INDXCOLOR, false>, /* former color16 */ \ render_scanline_##type<u16, INDXCOLOR, false>, /* former color16 */ \
render_scanline_##type<u16, INDXCOLOR, true>, \
render_scanline_##type<u32, INDXCOLOR, false>, /* former color32 */ \ render_scanline_##type<u32, INDXCOLOR, false>, /* former color32 */ \
render_scanline_##type<u32, STCKCOLOR, false>, /* for alpha blending */ \
},{ \
render_scanline_##type<u16, FULLCOLOR, true>, \
render_scanline_##type<u16, INDXCOLOR, true>, \
render_scanline_##type<u32, INDXCOLOR, true>, \ render_scanline_##type<u32, INDXCOLOR, true>, \
render_scanline_##type<u32, STCKCOLOR, true>, \
} \
} \ } \
#define bitmap_layer_render_functions(mode, ttype, w, h) \ #define bitmap_layer_render_functions(mode, ttype, w, h) \
@ -1522,7 +1519,6 @@ static u8 obj_alpha_count[160];
u32 dest \ u32 dest \
#define render_scanline_obj_extra_variables_copy(type) \ #define render_scanline_obj_extra_variables_copy(type) \
u32 bldcnt = read_ioreg(REG_BLDCNT); \
u32 dispcnt = read_ioreg(REG_DISPCNT); \ u32 dispcnt = read_ioreg(REG_DISPCNT); \
u32 obj_enable = read_ioreg(REG_WINOUT) >> 8; \ u32 obj_enable = read_ioreg(REG_WINOUT) >> 8; \
render_scanline_layer_functions_##type(); \ render_scanline_layer_functions_##type(); \
@ -1608,7 +1604,7 @@ static u8 obj_alpha_count[160];
if((copy_start < end) && (copy_end > start)) \ if((copy_start < end) && (copy_end > start)) \
{ \ { \
render_scanline_conditional_##type(copy_start, copy_end, copy_buffer, \ render_scanline_conditional_##type(copy_start, copy_end, copy_buffer, \
obj_enable, dispcnt, bldcnt, layer_renderers); \ obj_enable, layer_renderers); \
copy_ptr = copy_buffer + copy_start; \ copy_ptr = copy_buffer + copy_start; \
} \ } \
else \ else \
@ -1629,8 +1625,10 @@ static u8 obj_alpha_count[160];
#define render_scanline_obj_builder(combine_op, alpha_op, map_space, \ #define render_scanline_obj_builder(combine_op, alpha_op, map_space, \
partial_alpha_op) \ partial_alpha_op) \
static void render_scanline_obj_##alpha_op##_##map_space(u32 priority, \ static void render_scanline_obj_##alpha_op##_##map_space(u32 priority, \
u32 start, u32 end, render_scanline_dest_##alpha_op *scanline) \ u32 start, u32 end, void *raw_dst) \
{ \ { \
render_scanline_dest_##alpha_op *scanline = \
(render_scanline_dest_##alpha_op*)raw_dst; \
render_scanline_obj_extra_variables_##alpha_op(map_space); \ render_scanline_obj_extra_variables_##alpha_op(map_space); \
u32 obj_num, i; \ u32 obj_num, i; \
s32 obj_x, obj_y; \ s32 obj_x, obj_y; \
@ -1687,6 +1685,14 @@ 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);
static const tile_render_function obj_mode_renderers[5][2] = {
{ render_scanline_obj_normal_2D, render_scanline_obj_normal_1D },
{ render_scanline_obj_color16_2D, render_scanline_obj_color16_1D },
{ render_scanline_obj_color32_2D, render_scanline_obj_color32_1D },
{ render_scanline_obj_alpha_obj_2D, render_scanline_obj_alpha_obj_1D },
{ render_scanline_obj_partial_alpha_2D, render_scanline_obj_partial_alpha_1D },
};
// These are used for winobj rendering // 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);
@ -1818,35 +1824,6 @@ static void order_layers(u32 layer_flags, u32 vcnt)
} }
} }
#define fill_line(_start, _end) \
u32 i; \
\
for(i = _start; i < _end; i++) \
dest_ptr[i] = color; \
#define fill_line_color_normal() \
color = palette_ram_converted[color] \
#define fill_line_color_alpha() \
#define fill_line_color_color16() \
#define fill_line_color_color32() \
#define fill_line_builder(type) \
static void fill_line_##type(u16 color, render_scanline_dest_##type *dest_ptr,\
u32 start, u32 end) \
{ \
fill_line_color_##type(); \
fill_line(start, end); \
} \
fill_line_builder(normal);
fill_line_builder(alpha);
fill_line_builder(color16);
fill_line_builder(color32);
// Blending is performed by separating an RGB value into 0G0R0B (32 bit) // Blending is performed by separating an RGB value into 0G0R0B (32 bit)
// Since blending factors are at most 16, mult/add operations do not overflow // Since blending factors are at most 16, mult/add operations do not overflow
@ -2136,213 +2113,233 @@ static void expand_brighten_partial_alpha(u32 *screen_src_ptr, u16 *screen_dest_
// Render a target all the way with the background color as taken from the // Render a target all the way with the background color as taken from the
// palette. // palette.
#define fill_line_bg(type, dest, _start, _end) \ template<bool indexed, typename dsttype>
fill_line_##type(0, dest, _start, _end) \ void fill_line_background(u32 start, u32 end, void *scanline) {
dsttype *ptr = (dsttype*)scanline;
while (start < end)
if (indexed)
ptr[start++] = 0;
else
ptr[start++] = palette_ram_converted[0];
}
#define COL_EFFECT_NONE 0x0
#define COL_EFFECT_BLEND 0x1
#define COL_EFFECT_BRIGHT 0x2
#define COL_EFFECT_DARK 0x3
// Render all layers as they appear in the layer order. // Renders the backdrop color (ie. whenever no layer is active) applying
// any effects that might still apply (usually darken/brighten).
static void render_backdrop(u32 start, u32 end, u16 *scanline) {
u16 bldcnt = read_ioreg(REG_BLDCNT);
u16 pixcol = palette_ram_converted[0];
u32 effect = (bldcnt >> 6) & 0x03;
u32 bd_1st_target = ((bldcnt >> 0x5) & 0x01);
#define render_layers(tile_alpha, obj_alpha, dest) \ if (bd_1st_target && effect == COL_EFFECT_BRIGHT) {
{ \ u32 brightness = MIN(16, read_ioreg(REG_BLDY) & 0x1F);
current_layer = layer_order[0]; \
if(current_layer & 0x04) \
{ \
/* If the first one is OBJ render the background then render it. */ \
fill_line_bg(tile_alpha, dest, 0, 240); \
render_obj_layer(obj_alpha, dest, 0, 240); \
} \
else \
{ \
/* Otherwise render a base layer. */ \
layer_renderers[current_layer].tile_alpha##_render_base(current_layer, \
0, 240, dest); \
} \
\
/* Render the rest of the layers. */ \
for(layer_order_pos = 1; layer_order_pos < layer_count; layer_order_pos++) \
{ \
current_layer = layer_order[layer_order_pos]; \
if(current_layer & 0x04) \
{ \
render_obj_layer(obj_alpha, dest, 0, 240); \
} \
else \
{ \
layer_renderers[current_layer]. \
tile_alpha##_render_transparent(current_layer, 0, 240, dest); \
} \
} \
} \
#define render_condition_alpha \ // Unpack 16 bit pixel for fast blending operation
(((read_ioreg(REG_BLDALPHA) & 0x1F1F) != 0x001F) && \ u32 epixel = (pixcol | (pixcol << 16)) & BLND_MSK;
((read_ioreg(REG_BLDCNT) & 0x3F) != 0) && \ u32 pa = ((BLND_MSK * brightness) >> 4) & BLND_MSK; // White color
((read_ioreg(REG_BLDCNT) & 0x3F00) != 0)) \ u32 pb = ((epixel * (16 - brightness)) >> 4) & BLND_MSK; // Pixel color
epixel = (pa + pb) & BLND_MSK;
pixcol = (epixel >> 16) | epixel;
}
else if (bd_1st_target && effect == COL_EFFECT_DARK) {
u32 brightness = MIN(16, read_ioreg(REG_BLDY) & 0x1F);
u32 epixel = (pixcol | (pixcol << 16)) & BLND_MSK;
epixel = ((epixel * (16 - brightness)) >> 4) & BLND_MSK; // Pixel color
pixcol = (epixel >> 16) | epixel;
}
#define render_condition_fade \ // Fill the line with that color
(((read_ioreg(REG_BLDY) & 0x1F) != 0) && \ while (start < end)
((read_ioreg(REG_BLDCNT) & 0x3F) != 0)) \ scanline[start++] = pixcol;
}
#define render_layers_color_effect(renderer, layer_condition, \ #define RENDER_NORMAL 0
alpha_condition, fade_condition, _start, _end) \ #define RENDER_COL16 1
{ \ #define RENDER_COL32 2
if(layer_condition) \ #define RENDER_ALPHA 3
{ \
if(obj_alpha_count[read_ioreg(REG_VCOUNT)]) \ #define OBJ_NORMAL 0
{ \ #define OBJ_COL16 1
/* Render based on special effects mode. */ \ #define OBJ_COL32 2
u32 screen_buffer[240]; \ #define OBJ_ALPHA 3
switch((bldcnt >> 6) & 0x03) \ #define OBJ_PALPHA 4
{ \
/* Alpha blend */ \ void render_layers(u32 start, u32 end, void *dst_ptr, u32 enabled_layers,
case 0x01: \ u32 rend_mode, u32 obj_mode) {
{ \ u32 lnum;
if(alpha_condition) \ u16 dispcnt = read_ioreg(REG_DISPCNT);
{ \ bool obj_enabled = (enabled_layers & 0x10); // Objects are visible
renderer(alpha, alpha_obj, screen_buffer); \ // Renderers for this mode (affine or text pointers)
expand_blend(screen_buffer, scanline, _start, _end); \ const tile_layer_render_struct * r = tile_mode_renderers[dispcnt & 0x07];
return; \
} \ for (lnum = 0; lnum < layer_count; lnum++) {
break; \ u32 layer = layer_order[lnum];
} \ bool is_obj = layer & 0x4;
\ if (is_obj && obj_enabled) {
/* Fade to white */ \ // Draw an object first-layer, we need to fill backdrop color first!
case 0x02: \ if (rend_mode == RENDER_NORMAL)
{ \ fill_line_background<false, u16>(start, end, dst_ptr);
if(fade_condition) \ else if (rend_mode == OBJ_COL16)
{ \ fill_line_background<true, u16>(start, end, dst_ptr);
renderer(color32, partial_alpha, screen_buffer); \ else
expand_brighten_partial_alpha(screen_buffer, scanline, \ fill_line_background<true, u32>(start, end, dst_ptr);
_start, _end); \
return; \ obj_mode_renderers[obj_mode][(dispcnt >> 6) & 1](layer & 0x3, start, end, dst_ptr);
} \ break;
break; \ }
} \ else if (!is_obj && ((1 << layer) & enabled_layers)) {
\ // Draw base layer
/* Fade to black */ \ r[layer].base[rend_mode](layer, start, end, dst_ptr);
case 0x03: \ break;
{ \ }
if(fade_condition) \ }
{ \
renderer(color32, partial_alpha, screen_buffer); \ if (lnum == layer_count) {
expand_darken_partial_alpha(screen_buffer, scanline, \ // Render background, no layers are active!
_start, _end); \ // TODO improve this code.
return; \ if (rend_mode == RENDER_NORMAL)
} \ fill_line_background<false, u16>(start, end, dst_ptr);
break; \ else if (rend_mode == OBJ_COL16)
} \ fill_line_background<true, u16>(start, end, dst_ptr);
} \ else
\ fill_line_background<true, u32>(start, end, dst_ptr);
renderer(color32, partial_alpha, screen_buffer); \
expand_blend(screen_buffer, scanline, _start, _end); \ return;
} \ }
else \
{ \ while (++lnum < layer_count) {
/* Render based on special effects mode. */ \ u32 layer = layer_order[lnum];
switch((bldcnt >> 6) & 0x03) \ bool is_obj = layer & 0x4;
{ \ if (is_obj && obj_enabled)
/* Alpha blend */ \ obj_mode_renderers[obj_mode][(dispcnt >> 6) & 1](layer & 0x3, start, end, dst_ptr);
case 0x01: \ else if (!is_obj && ((1 << layer) & enabled_layers))
{ \ r[layer].trans[rend_mode](layer, start, end, dst_ptr);
if(alpha_condition) \ }
{ \ }
u32 screen_buffer[240]; \
renderer(alpha, alpha_obj, screen_buffer); \ // Renders a partial scanline without using any coloring effects (with the
expand_blend(screen_buffer, scanline, _start, _end); \ // exception of OBJ blending).
return; \
} \ static void render_color_no_effect(
break; \ u32 start, u32 end, u16* scanline, u32 enable_flags
} \ ) {
\ bool obj_blend = obj_alpha_count[read_ioreg(REG_VCOUNT)] > 0;
/* Fade to white */ \
case 0x02: \ // Default rendering mode, without layer effects (except perhaps sprites).
{ \ if (obj_blend) {
if(fade_condition) \ u32 screen_buffer[240];
{ \ render_layers(start, end, screen_buffer, enable_flags, RENDER_COL32, OBJ_PALPHA);
renderer(color16, color16, scanline); \ expand_blend(screen_buffer, scanline, start, end);
expand_brighten(scanline, scanline, _start, _end); \ } else {
return; \ render_layers(start, end, scanline, enable_flags, RENDER_NORMAL, OBJ_NORMAL);
} \ }
break; \ }
} \
\ // Renders all layers honoring color effects (blending, brighten/darken).
/* Fade to black */ \ // It uses different rendering routines depending on the coloring effect
case 0x03: \ // requirements, speeding up common cases where no effects are used.
{ \
if(fade_condition) \ // No effects use NORMAL mode (RBB565 color is written on the buffer).
{ \ // For blending, we use BLEND mode to record the two top-most pixels.
renderer(color16, color16, scanline); \ // For other effects we use COLOR16, which records an indexed color in the
expand_darken(scanline, scanline, _start, _end); \ // buffer (used for darken/brighten effects at later passes) or COLOR32,
return; \ // which similarly uses an indexed color for rendering but recording one
} \ // color for the background and another one for the object layer.
break; \
} \ static void render_color_effect(
} \ u32 start, u32 end, u16* scanline, u32 enable_flags = 0x1F /* all enabled */
\ ) {
renderer(normal, normal, scanline); \ bool obj_blend = obj_alpha_count[read_ioreg(REG_VCOUNT)] > 0;
expand_normal(scanline, _start, _end); \ u16 bldcnt = read_ioreg(REG_BLDCNT);
} \
} \ switch((bldcnt >> 6) & 0x03) {
else \ case COL_EFFECT_BRIGHT:
{ \ {
u32 pixel_top = palette_ram_converted[0]; \ // If no layers are 1st target, no effect will really happen.
switch((bldcnt >> 6) & 0x03) \ bool some_1st_tgt = (read_ioreg(REG_BLDCNT) & 0x3F) != 0;
{ \ // If the factor is zero, it's the same as "regular" rendering.
/* Fade to white */ \ bool non_zero_blend = (read_ioreg(REG_BLDY) & 0x1F) != 0;
case 0x02: \ if (some_1st_tgt && non_zero_blend) {
{ \ if (obj_blend) {
if(color_combine_mask_a(5)) \ u32 screen_buffer[240];
{ \ render_layers(start, end, screen_buffer, enable_flags, RENDER_COL32, OBJ_PALPHA);
u32 blend = read_ioreg(REG_BLDY) & 0x1F; \ expand_brighten_partial_alpha(screen_buffer, scanline, start, end);
u32 upper; \ } else {
\ render_layers(start, end, scanline, enable_flags, RENDER_COL16, OBJ_COL16);
if(blend > 16) \ expand_brighten(scanline, scanline, start, end);
blend = 16; \ }
\ return;
upper = ((BLND_MSK * blend) >> 4) & BLND_MSK; \ }
blend = 16 - blend; \ }
\ break;
expand_pixel_no_dest(brighten, pixel_top); \
} \ case COL_EFFECT_DARK:
break; \ {
} \ // If no layers are 1st target, no effect will really happen.
\ bool some_1st_tgt = (read_ioreg(REG_BLDCNT) & 0x3F) != 0;
/* Fade to black */ \ // If the factor is zero, it's the same as "regular" rendering.
case 0x03: \ bool non_zero_blend = (read_ioreg(REG_BLDY) & 0x1F) != 0;
{ \ if (some_1st_tgt && non_zero_blend) {
if(color_combine_mask_a(5)) \ if (obj_blend) {
{ \ u32 screen_buffer[240];
s32 blend = 16 - (read_ioreg(REG_BLDY) & 0x1F); \ render_layers(start, end, screen_buffer, enable_flags, RENDER_COL32, OBJ_PALPHA);
\ expand_darken_partial_alpha(screen_buffer, scanline, start, end);
if(blend < 0) \ } else {
blend = 0; \ render_layers(start, end, scanline, enable_flags, RENDER_COL16, OBJ_COL16);
\ expand_darken(scanline, scanline, start, end);
expand_pixel_no_dest(darken, pixel_top); \ }
} \ return;
break; \ }
} \ }
} \ break;
fill_line_color16(pixel_top, scanline, _start, _end); \
} \ case COL_EFFECT_BLEND:
} \ {
// If no layers are 1st or 2nd target, no effect will really happen.
bool some_1st_tgt = (read_ioreg(REG_BLDCNT) & 0x003F) != 0;
bool some_2nd_tgt = (read_ioreg(REG_BLDCNT) & 0x3F00) != 0;
// If 1st target is 100% opacity and 2nd is 0%, just render regularly.
bool non_trns_tgt = (read_ioreg(REG_BLDALPHA) & 0x1F1F) != 0x001F;
if (some_1st_tgt && some_2nd_tgt && non_trns_tgt) {
u32 screen_buffer[240];
render_layers(start, end, screen_buffer, enable_flags, RENDER_ALPHA, OBJ_ALPHA);
expand_blend(screen_buffer, scanline, start, end);
return;
}
}
break;
case COL_EFFECT_NONE:
// Default case, see below.
break;
};
// Default case, just a regular no-effects render.
render_color_no_effect(start, end, scanline, enable_flags);
}
// 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> template<bool tiled>
static void render_scanline(u16 *scanline, u32 dispcnt) static void render_scanline(u16 *scanline)
{ {
u32 current_layer; u32 current_layer;
u32 layer_order_pos; u32 layer_order_pos;
if (tiled) { if (tiled) {
u32 bldcnt = read_ioreg(REG_BLDCNT); if (layer_count)
render_scanline_layer_functions_tile(); render_color_effect(0, 240, scanline);
else
render_layers_color_effect(render_layers, layer_count, render_backdrop(0, 240, scanline);
render_condition_alpha, render_condition_fade, 0, 240);
} else { } else {
render_scanline_layer_functions_bitmap(); u16 dispcnt = read_ioreg(REG_DISPCNT);
fill_line_bg(normal, scanline, 0, 240); const bitmap_layer_render_struct *lrend = &bitmap_mode_renderers[(dispcnt & 0x07) - 3];
fill_line_background<false, u16>(0, 240, scanline);
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++)
{ {
@ -2357,126 +2354,31 @@ static void render_scanline(u16 *scanline, u32 dispcnt)
s32 dy = (s16)read_ioreg(REG_BG2PC); s32 dy = (s16)read_ioreg(REG_BG2PC);
if (dy) if (dy)
layer_renderers->affine_render(0, 240, scanline); lrend->affine_render(0, 240, scanline);
else if (dx == 256) else if (dx == 256)
layer_renderers->blit_render(0, 240, scanline); lrend->blit_render(0, 240, scanline);
else else
layer_renderers->scale_render(0, 240, scanline); lrend->scale_render(0, 240, scanline);
} }
} }
} }
} }
// Render layers from start to end based on if they're allowed in the
// enable flags.
#define render_layers_conditional(tile_alpha, obj_alpha, dest) \
{ \
__label__ skip; \
current_layer = layer_order[layer_order_pos]; \
/* If OBJ aren't enabled skip to the first non-OBJ layer */ \
if(!(enable_flags & 0x10)) \
{ \
while((current_layer & 0x04) || !((1 << current_layer) & enable_flags)) \
{ \
layer_order_pos++; \
current_layer = layer_order[layer_order_pos]; \
\
/* Oops, ran out of layers, render the background. */ \
if(layer_order_pos == layer_count) \
{ \
fill_line_bg(tile_alpha, dest, start, end); \
goto skip; \
} \
} \
\
/* Render the first valid layer */ \
layer_renderers[current_layer].tile_alpha##_render_base(current_layer, \
start, end, dest); \
\
layer_order_pos++; \
\
/* Render the rest of the layers if active, skipping OBJ ones. */ \
for(; layer_order_pos < layer_count; layer_order_pos++) \
{ \
current_layer = layer_order[layer_order_pos]; \
if(!(current_layer & 0x04) && ((1 << current_layer) & enable_flags)) \
{ \
layer_renderers[current_layer]. \
tile_alpha##_render_transparent(current_layer, start, end, dest); \
} \
} \
} \
else \
{ \
/* Find the first active layer, skip all of the inactive ones */ \
while(!((current_layer & 0x04) || ((1 << current_layer) & enable_flags))) \
{ \
layer_order_pos++; \
current_layer = layer_order[layer_order_pos]; \
\
/* Oops, ran out of layers, render the background. */ \
if(layer_order_pos == layer_count) \
{ \
fill_line_bg(tile_alpha, dest, start, end); \
goto skip; \
} \
} \
\
if(current_layer & 0x04) \
{ \
/* If the first one is OBJ render the background then render it. */ \
fill_line_bg(tile_alpha, dest, start, end); \
render_obj_layer(obj_alpha, dest, start, end); \
} \
else \
{ \
/* Otherwise render a base layer. */ \
layer_renderers[current_layer]. \
tile_alpha##_render_base(current_layer, start, end, dest); \
} \
\
layer_order_pos++; \
\
/* Render the rest of the layers. */ \
for(; layer_order_pos < layer_count; layer_order_pos++) \
{ \
current_layer = layer_order[layer_order_pos]; \
if(current_layer & 0x04) \
{ \
render_obj_layer(obj_alpha, dest, start, end); \
} \
else \
{ \
if(enable_flags & (1 << current_layer)) \
{ \
layer_renderers[current_layer]. \
tile_alpha##_render_transparent(current_layer, start, end, dest); \
} \
} \
} \
} \
\
skip: \
; \
} \
// Render all of the BG and OBJ in a tiled scanline from start to end ONLY if // Render all of the BG and OBJ in a tiled scanline from start to end ONLY if
// enable_flag allows that layer/OBJ. Also conditionally render color effects. // enable_flag allows that layer/OBJ. Also conditionally render color effects.
static void render_scanline_conditional_tile(u32 start, u32 end, u16 *scanline, static void render_scanline_conditional_tile(u32 start, u32 end, u16 *scanline,
u32 enable_flags, u32 dispcnt, u32 bldcnt, const tile_layer_render_struct u32 enable_flags, const tile_layer_render_struct *layer_renderers)
*layer_renderers)
{ {
u32 current_layer; if (layer_count && (enable_flags & 0x1F)) {
u32 layer_order_pos = 0; bool effects_enabled = enable_flags & 0x20; // Window bit for effects.
if (effects_enabled)
render_layers_color_effect(render_layers_conditional, render_color_effect(start, end, scanline, enable_flags);
(layer_count && (enable_flags & 0x1F)), else
((enable_flags & 0x20) && render_condition_alpha), render_color_no_effect(start, end, scanline, enable_flags);
((enable_flags & 0x20) && render_condition_fade), start, end); }
else
render_backdrop(start, end, scanline);
} }
@ -2484,13 +2386,13 @@ static void render_scanline_conditional_tile(u32 start, u32 end, u16 *scanline,
// enable_flag allows that layer/OBJ. Also conditionally render color effects. // enable_flag allows that layer/OBJ. Also conditionally render color effects.
static void render_scanline_conditional_bitmap(u32 start, u32 end, u16 *scanline, static void render_scanline_conditional_bitmap(u32 start, u32 end, u16 *scanline,
u32 enable_flags, u32 dispcnt, u32 bldcnt, const bitmap_layer_render_struct u32 enable_flags, const bitmap_layer_render_struct *layer_renderers)
*layer_renderers)
{ {
u16 dispcnt = read_ioreg(REG_DISPCNT);
u32 current_layer; u32 current_layer;
u32 layer_order_pos; u32 layer_order_pos;
fill_line_bg(normal, scanline, start, end); fill_line_background<false, u16>(start, end, scanline);
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++)
{ {
@ -2539,27 +2441,27 @@ inline bool in_window_y(u32 vcount, u32 top, u32 bottom) {
template <bool tiled> template <bool tiled>
static inline void render_scanline_conditional(u32 start, u32 end, static inline void render_scanline_conditional(u32 start, u32 end,
u16 *scanline, u32 enable_flags, u32 dispcnt, u32 bldcnt) u16 *scanline, u32 enable_flags)
{ {
u16 dispcnt = read_ioreg(REG_DISPCNT);
if (tiled) { if (tiled) {
const tile_layer_render_struct *layer_renderers = tile_mode_renderers[dispcnt & 0x07]; 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); render_scanline_conditional_tile(start, end, scanline, enable_flags, layer_renderers);
} }
else { else {
const bitmap_layer_render_struct *layer_renderers = &bitmap_mode_renderers[(dispcnt & 0x07) - 3]; 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_scanline_conditional_bitmap(start, end, scanline, enable_flags, layer_renderers);
} }
} }
// Renders window1 (low priority window) and outside/obj areas for a given range. // Renders window1 (low priority window) and outside/obj areas for a given range.
template <bool tiled> template <bool tiled>
static void render_windowobj_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end) static void render_windowobj_pass(u16 *scanline, u32 start, u32 end)
{ {
u32 bldcnt = read_ioreg(REG_BLDCNT); u16 dispcnt = read_ioreg(REG_DISPCNT);
u32 winout = read_ioreg(REG_WINOUT); u32 winout = read_ioreg(REG_WINOUT);
u32 wndout_enable = winout & 0x3F; u32 wndout_enable = winout & 0x3F;
render_scanline_conditional<tiled>(start, end, scanline, render_scanline_conditional<tiled>(start, end, scanline, wndout_enable);
wndout_enable, dispcnt, bldcnt);
if (dispcnt >> 15) { if (dispcnt >> 15) {
// Perform the actual object rendering in copy mode // Perform the actual object rendering in copy mode
@ -2579,19 +2481,19 @@ static void render_windowobj_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end
// Renders window1 (low priority window) and outside/obj areas for a given range. // Renders window1 (low priority window) and outside/obj areas for a given range.
template <bool tiled> template <bool tiled>
static void render_window1_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end) static void render_window1_pass(u16 *scanline, u32 start, u32 end)
{ {
u32 bldcnt = read_ioreg(REG_BLDCNT); u16 dispcnt = read_ioreg(REG_DISPCNT);
u32 winout = read_ioreg(REG_WINOUT); u32 winout = read_ioreg(REG_WINOUT);
u32 wndout_enable = winout & 0x3F; u32 wndout_enable = winout & 0x3F;
switch (dispcnt >> 14) { switch (dispcnt >> 14) {
case 0x0: // No Win1 nor WinObj case 0x0: // No Win1 nor WinObj
render_scanline_conditional<tiled>( render_scanline_conditional<tiled>(
start, end, scanline, wndout_enable, dispcnt, bldcnt); start, end, scanline, wndout_enable);
break; break;
case 0x2: // Only winobj enabled, render it. case 0x2: // Only winobj enabled, render it.
render_windowobj_pass<tiled>(scanline, dispcnt, start, end); render_windowobj_pass<tiled>(scanline, start, end);
break; break;
case 0x1: case 0x3: // Win1 is enabled (and perhaps WinObj too) case 0x1: case 0x3: // Win1 is enabled (and perhaps WinObj too)
{ {
@ -2607,8 +2509,7 @@ static void render_window1_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end)
if (!in_window_y(vcount, win_top, win_bot) || (win_l == win_r)) if (!in_window_y(vcount, win_top, win_bot) || (win_l == win_r))
// Window1 is completely out, just render all out. // Window1 is completely out, just render all out.
render_windowobj_pass<tiled>( render_windowobj_pass<tiled>(scanline, start, end);
scanline, dispcnt, start, end);
else { else {
// Render win1 withtin the clipped range // Render win1 withtin the clipped range
// Enable bits for stuff inside the window (and outside) // Enable bits for stuff inside the window (and outside)
@ -2619,24 +2520,24 @@ static void render_window1_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end)
if (win_l < win_r) { if (win_l < win_r) {
// Render [start, win_l) range (which is outside the window) // Render [start, win_l) range (which is outside the window)
if (win_l != start) if (win_l != start)
render_windowobj_pass<tiled>(scanline, dispcnt, start, win_l); render_windowobj_pass<tiled>(scanline, start, win_l);
// Render the actual window0 pixels // Render the actual window0 pixels
render_scanline_conditional<tiled>( render_scanline_conditional<tiled>(
win_l, win_r, scanline, wnd1_enable, dispcnt, bldcnt); win_l, win_r, scanline, wnd1_enable);
// Render the [win_l, end] range (outside) // Render the [win_l, end] range (outside)
if (win_r != end) if (win_r != end)
render_windowobj_pass<tiled>(scanline, dispcnt, win_r, end); render_windowobj_pass<tiled>(scanline, win_r, end);
} else { } else {
// Render [0, win_r) range (which is "inside" window0) // Render [0, win_r) range (which is "inside" window0)
if (win_r != start) if (win_r != start)
render_scanline_conditional<tiled>( render_scanline_conditional<tiled>(
start, win_r, scanline, wnd1_enable, dispcnt, bldcnt); start, win_r, scanline, wnd1_enable);
// The actual window is now outside, render recursively // The actual window is now outside, render recursively
render_windowobj_pass<tiled>(scanline, dispcnt, win_r, win_l); render_windowobj_pass<tiled>(scanline, win_r, win_l);
// Render the [win_l, 240] range ("inside") // Render the [win_l, 240] range ("inside")
if (win_l != end) if (win_l != end)
render_scanline_conditional<tiled>( render_scanline_conditional<tiled>(
win_l, end, scanline, wnd1_enable, dispcnt, bldcnt); win_l, end, scanline, wnd1_enable);
} }
} }
} }
@ -2648,9 +2549,8 @@ static void render_window1_pass(u16 *scanline, u16 dispcnt, u32 start, u32 end)
// on the area that falls outside. It will call the above function for // on the area that falls outside. It will call the above function for
// outside areas to "recursively" render segments. // outside areas to "recursively" render segments.
template <bool tiled> template <bool tiled>
static void render_window0_pass(u16 *scanline, u16 dispcnt) static void render_window0_pass(u16 *scanline)
{ {
u32 bldcnt = read_ioreg(REG_BLDCNT);
u32 vcount = read_ioreg(REG_VCOUNT); u32 vcount = read_ioreg(REG_VCOUNT);
// Check the Y coordinates to check if they fall in the right row // Check the Y coordinates to check if they fall in the right row
u32 win_top = read_ioreg(REG_WINxV(0)) >> 8; u32 win_top = read_ioreg(REG_WINxV(0)) >> 8;
@ -2661,7 +2561,7 @@ static void render_window0_pass(u16 *scanline, u16 dispcnt)
if (!in_window_y(vcount, win_top, win_bot) || (win_l == win_r)) if (!in_window_y(vcount, win_top, win_bot) || (win_l == win_r))
// No windowing, everything is "outside", just render win1. // No windowing, everything is "outside", just render win1.
render_window1_pass<tiled>(scanline, dispcnt, 0, 240); render_window1_pass<tiled>(scanline, 0, 240);
else { else {
u32 winin = read_ioreg(REG_WININ); u32 winin = read_ioreg(REG_WININ);
// Enable bits for stuff inside the window // Enable bits for stuff inside the window
@ -2671,24 +2571,24 @@ static void render_window0_pass(u16 *scanline, u16 dispcnt)
if (win_l < win_r) { if (win_l < win_r) {
// Render [0, win_l) range (which is outside the window) // Render [0, win_l) range (which is outside the window)
if (win_l) if (win_l)
render_window1_pass<tiled>(scanline, dispcnt, 0, win_l); render_window1_pass<tiled>(scanline, 0, win_l);
// Render the actual window0 pixels // Render the actual window0 pixels
render_scanline_conditional<tiled>( render_scanline_conditional<tiled>(
win_l, win_r, scanline, wnd0_enable, dispcnt, bldcnt); win_l, win_r, scanline, wnd0_enable);
// Render the [win_l, 240] range (outside) // Render the [win_l, 240] range (outside)
if (win_r != 240) if (win_r != 240)
render_window1_pass<tiled>(scanline, dispcnt, win_r, 240); render_window1_pass<tiled>(scanline, win_r, 240);
} else { } else {
// Render [0, win_r) range (which is "inside" window0) // Render [0, win_r) range (which is "inside" window0)
if (win_r) if (win_r)
render_scanline_conditional<tiled>( render_scanline_conditional<tiled>(
0, win_r, scanline, wnd0_enable, dispcnt, bldcnt); 0, win_r, scanline, wnd0_enable);
// The actual window is now outside, render recursively // The actual window is now outside, render recursively
render_window1_pass<tiled>(scanline, dispcnt, win_r, win_l); render_window1_pass<tiled>(scanline, win_r, win_l);
// Render the [win_l, 240] range ("inside") // Render the [win_l, 240] range ("inside")
if (win_l != 240) if (win_l != 240)
render_scanline_conditional<tiled>( render_scanline_conditional<tiled>(
win_l, 240, scanline, wnd0_enable, dispcnt, bldcnt); win_l, 240, scanline, wnd0_enable);
} }
} }
} }
@ -2697,8 +2597,9 @@ static void render_window0_pass(u16 *scanline, u16 dispcnt)
// Renders a full scaleline, taking into consideration windowing effects. // Renders a full scaleline, taking into consideration windowing effects.
// Breaks the rendering step into N steps, for each windowed region. // Breaks the rendering step into N steps, for each windowed region.
template <bool tiled> template <bool tiled>
static void render_scanline_window(u16 *scanline, u16 dispcnt) static void render_scanline_window(u16 *scanline)
{ {
u16 dispcnt = read_ioreg(REG_DISPCNT);
u32 win_ctrl = (dispcnt >> 13); u32 win_ctrl = (dispcnt >> 13);
// Priority decoding for windows // Priority decoding for windows
@ -2706,19 +2607,19 @@ static void render_scanline_window(u16 *scanline, u16 dispcnt)
case 0x1: case 0x3: case 0x5: case 0x7: case 0x1: case 0x3: case 0x5: case 0x7:
// Window 0 is enabled, call the win0 render function. It does recursively // Window 0 is enabled, call the win0 render function. It does recursively
// check for window 1 and Obj, so no worries. // check for window 1 and Obj, so no worries.
render_window0_pass<tiled>(scanline, dispcnt); render_window0_pass<tiled>(scanline);
break; break;
case 0x2: case 0x6: case 0x2: case 0x6:
// Window 1 is active, call the window1 renderer. // Window 1 is active, call the window1 renderer.
render_window1_pass<tiled>(scanline, dispcnt, 0, 240); render_window1_pass<tiled>(scanline, 0, 240);
break; break;
case 0x4: case 0x4:
// Only winobj seems active // Only winobj seems active
render_windowobj_pass<tiled>(scanline, dispcnt, 0, 240); render_windowobj_pass<tiled>(scanline, 0, 240);
break; break;
case 0x0: case 0x0:
// No windows are active? // No windows are active?
render_scanline<tiled>(scanline, dispcnt); render_scanline<tiled>(scanline);
break; break;
} }
} }
@ -2758,15 +2659,15 @@ void update_scanline(void)
// If the screen is in in forced blank draw pure white. // If the screen is in in forced blank draw pure white.
if(dispcnt & 0x80) if(dispcnt & 0x80)
{ {
fill_line_color16(0xFFFF, screen_offset, 0, 240); memset(screen_offset, 0xff, 240*sizeof(u16));
} }
else else
{ {
// Modes 0..2 are tiled modes, 3..5 are bitmap-based modes. // 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); render_scanline_window<true>(screen_offset);
else else
render_scanline_window<false>(screen_offset, dispcnt); render_scanline_window<false>(screen_offset);
} }
affine_reference_x[0] += (s16)read_ioreg(REG_BG2PB); affine_reference_x[0] += (s16)read_ioreg(REG_BG2PB);