// license:BSD-3-Clause // copyright-holders:Ryan Holtz /****************************************************************************** SGI/Nintendo Reality Display Processor Blend Unit (BL) ------------------- by Ryan Holtz based on initial C code by Ville Linde contains additional improvements from angrylion, Ziggy, Gonetz and Orkin ******************************************************************************/ #include "emu.h" #include "includes/n64.h" #include "video/n64.h" n64_blender_t::n64_blender_t() { blend1[0] = &n64_blender_t::cycle1_noblend_noacvg_nodither; blend1[1] = &n64_blender_t::cycle1_noblend_noacvg_dither; blend1[2] = &n64_blender_t::cycle1_noblend_acvg_nodither; blend1[3] = &n64_blender_t::cycle1_noblend_acvg_dither; blend1[4] = &n64_blender_t::cycle1_blend_noacvg_nodither; blend1[5] = &n64_blender_t::cycle1_blend_noacvg_dither; blend1[6] = &n64_blender_t::cycle1_blend_acvg_nodither; blend1[7] = &n64_blender_t::cycle1_blend_acvg_dither; blend2[0] = &n64_blender_t::cycle2_noblend_noacvg_nodither; blend2[1] = &n64_blender_t::cycle2_noblend_noacvg_dither; blend2[2] = &n64_blender_t::cycle2_noblend_acvg_nodither; blend2[3] = &n64_blender_t::cycle2_noblend_acvg_dither; blend2[4] = &n64_blender_t::cycle2_blend_noacvg_nodither; blend2[5] = &n64_blender_t::cycle2_blend_noacvg_dither; blend2[6] = &n64_blender_t::cycle2_blend_acvg_nodither; blend2[7] = &n64_blender_t::cycle2_blend_acvg_dither; for (int value = 0; value < 256; value++) { for (int dither = 0; dither < 8; dither++) { m_color_dither[(value << 3) | dither] = (uint8_t)dither_color(value, dither); m_alpha_dither[(value << 3) | dither] = (uint8_t)dither_alpha(value, dither); } } } int32_t n64_blender_t::dither_alpha(int32_t alpha, int32_t dither) { return min(alpha + dither, 0xff); } int32_t n64_blender_t::dither_color(int32_t color, int32_t dither) { if ((color & 7) > dither) { color = (color & 0xf8) + 8; if (color > 247) { color = 255; } } return color; } bool n64_blender_t::test_for_reject(rdp_span_aux* userdata, const rdp_poly_state& object) { if (alpha_reject(userdata, object)) { return true; } if (object.m_other_modes.antialias_en ? !userdata->m_current_pix_cvg : !userdata->m_current_cvg_bit) { return true; } return false; } bool n64_blender_t::alpha_reject(rdp_span_aux* userdata, const rdp_poly_state& object) { switch (object.m_other_modes.alpha_dither_mode) { case 0: case 1: return false; case 2: return userdata->m_pixel_color.get_a() < userdata->m_blend_color.get_a(); case 3: return userdata->m_pixel_color.get_a() < (machine().rand() & 0xff); default: return false; } } bool n64_blender_t::cycle1_noblend_noacvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[((uint8_t)userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } blended_pixel.set(*userdata->m_color_inputs.blender1a_rgb[0]); return true; } bool n64_blender_t::cycle1_noblend_noacvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[((uint8_t)userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } rgbaint_t index(*userdata->m_color_inputs.blender1a_rgb[0]); index.shl_imm(3); index.or_imm(dith); index.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[index.get_r32()], m_color_dither[index.get_g32()], m_color_dither[index.get_b32()]); return true; } bool n64_blender_t::cycle1_noblend_acvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } blended_pixel.set(*userdata->m_color_inputs.blender1a_rgb[0]); return true; } bool n64_blender_t::cycle1_noblend_acvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } rgbaint_t index(*userdata->m_color_inputs.blender1a_rgb[0]); index.shl_imm(3); index.or_imm(dith); index.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[index.get_r32()], m_color_dither[index.get_g32()], m_color_dither[index.get_b32()]); return true; } bool n64_blender_t::cycle1_blend_noacvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[((uint8_t)userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } blend_with_partial_reject(blended_pixel, 0, partialreject, sel0, userdata, object); return true; } bool n64_blender_t::cycle1_blend_noacvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[((uint8_t)userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } color_t rgb; blend_with_partial_reject(rgb, 0, partialreject, sel0, userdata, object); rgb.shl_imm(3); rgb.or_imm(dith); rgb.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[rgb.get_r32()], m_color_dither[rgb.get_g32()], m_color_dither[rgb.get_b32()]); return true; } bool n64_blender_t::cycle1_blend_acvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } blend_with_partial_reject(blended_pixel, 0, partialreject, sel0, userdata, object); return true; } bool n64_blender_t::cycle1_blend_acvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } color_t rgb; blend_with_partial_reject(rgb, 0, partialreject, sel0, userdata, object); rgb.shl_imm(3); rgb.or_imm(dith); rgb.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[rgb.get_r32()], m_color_dither[rgb.get_g32()], m_color_dither[rgb.get_b32()]); return true; } bool n64_blender_t::cycle2_noblend_noacvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[((uint8_t)userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); blended_pixel.set(*userdata->m_color_inputs.blender1a_rgb[1]); return true; } bool n64_blender_t::cycle2_noblend_noacvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[((uint8_t)userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - (uint8_t)userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); rgbaint_t index(*userdata->m_color_inputs.blender1a_rgb[1]); index.shl_imm(3); index.or_imm(dith); index.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[index.get_r32()], m_color_dither[index.get_g32()], m_color_dither[index.get_b32()]); return true; } bool n64_blender_t::cycle2_noblend_acvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); blended_pixel.set(*userdata->m_color_inputs.blender1a_rgb[1]); return true; } bool n64_blender_t::cycle2_noblend_acvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); rgbaint_t index(*userdata->m_color_inputs.blender1a_rgb[1]); index.shl_imm(3); index.or_imm(dith); index.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[index.get_r32()], m_color_dither[index.get_g32()], m_color_dither[index.get_b32()]); return true; } bool n64_blender_t::cycle2_blend_noacvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[((uint8_t)userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[((uint8_t)userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); blend_with_partial_reject(blended_pixel, 1, partialreject, sel1, userdata, object); return true; } bool n64_blender_t::cycle2_blend_noacvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_pixel_color.set_a(m_alpha_dither[(userdata->m_pixel_color.get_a() << 3) | adseed]); userdata->m_shade_color.set_a(m_alpha_dither[(userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); color_t rgb; blend_with_partial_reject(rgb, 1, partialreject, sel1, userdata, object); rgb.shl_imm(3); rgb.or_imm(dith); rgb.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[rgb.get_r32()], m_color_dither[rgb.get_g32()], m_color_dither[rgb.get_b32()]); return true; } bool n64_blender_t::cycle2_blend_acvg_nodither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[(userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); blend_with_partial_reject(blended_pixel, 1, partialreject, sel1, userdata, object); return true; } bool n64_blender_t::cycle2_blend_acvg_dither(color_t& blended_pixel, int dith, int adseed, int partialreject, int sel0, int sel1, rdp_span_aux* userdata, const rdp_poly_state& object) { userdata->m_shade_color.set_a(m_alpha_dither[(userdata->m_shade_color.get_a() << 3) | adseed]); if (test_for_reject(userdata, object)) { return false; } userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[0]->get_a()); blend_pipe(0, sel0, userdata->m_blended_pixel_color, userdata, object); userdata->m_blended_pixel_color.set_a(userdata->m_pixel_color.get_a()); color_t rgb; blend_with_partial_reject(rgb, 1, partialreject, sel1, userdata, object); rgb.shl_imm(3); rgb.or_imm(dith); rgb.and_imm(0x7ff); blended_pixel.set(0, m_color_dither[rgb.get_r32()], m_color_dither[rgb.get_g32()], m_color_dither[rgb.get_b32()]); return true; } void n64_blender_t::blend_with_partial_reject(color_t& out, int32_t cycle, int32_t partialreject, int32_t select, rdp_span_aux* userdata, const rdp_poly_state& object) { if (partialreject && userdata->m_pixel_color.get_a() >= 0xff) { out.set(*userdata->m_color_inputs.blender1a_rgb[cycle]); } else { userdata->m_inv_pixel_color.set_a(0xff - userdata->m_color_inputs.blender1b_a[cycle]->get_a()); blend_pipe(cycle, select, out, userdata, object); } } void n64_blender_t::blend_pipe(const int cycle, const int special, color_t& out, rdp_span_aux* userdata, const rdp_poly_state& object) { const int32_t mask = 0xff &~ (0x73 * special); const int32_t shift_a = 3 + userdata->m_shift_a * special; const int32_t shift_b = 3 + userdata->m_shift_b * special; const int32_t blend1a = (userdata->m_color_inputs.blender1b_a[cycle]->get_a() >> shift_a) & mask; const int32_t blend2a = (userdata->m_color_inputs.blender2b_a[cycle]->get_a() >> shift_b) & mask; const int32_t special_shift = special << 1; rgbaint_t temp(*userdata->m_color_inputs.blender1a_rgb[cycle]); temp.mul_imm(blend1a); rgbaint_t secondary(*userdata->m_color_inputs.blender2a_rgb[cycle]); rgbaint_t other(*userdata->m_color_inputs.blender2a_rgb[cycle]); other.mul_imm(blend2a); temp.add(other); secondary.shl_imm(special_shift); temp.add(secondary); temp.shr_imm(object.m_other_modes.blend_shift); int32_t factor_sum = 0; if (!object.m_other_modes.force_blend) { factor_sum = ((blend1a >> 2) + (blend2a >> 2) + 1) & 0xf; if (factor_sum) { temp.set_r(temp.get_r32() / factor_sum); temp.set_g(temp.get_g32() / factor_sum); temp.set_b(temp.get_b32() / factor_sum); } else { temp.set(0, 0xff, 0xff, 0xff); } } temp.min(255); out.set(temp); } inline int32_t n64_blender_t::min(const int32_t x, const int32_t min) { if (x < min) { return x; } return min; }