NESHawk: more efficient sprite check

Gives a ~5% speed boost by not checking every sprite all the time.
This commit is contained in:
alyosha-tas 2017-08-27 18:17:10 -04:00 committed by GitHub
parent 0e37c12c4f
commit 3961292294
1 changed files with 112 additions and 78 deletions

View File

@ -1,6 +1,7 @@
//TODO - correctly emulate PPU OFF state
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using System;
namespace BizHawk.Emulation.Cores.Nintendo.NES
@ -39,6 +40,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
public byte[] soam = new byte[512]; // in a real nes, this would only be 32, but we wish to allow more then 8 sprites per scanline
public bool reg_2001_color_disable_latch; // the value used here is taken
public bool ppu_was_on;
public byte[,] sl_sprites = new byte[3, 256];
// installing vram address is delayed after second write to 2006, set this up here
public int install_2006;
@ -67,12 +69,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
Read_bgdata(i, ref bgdata);
runppu(1);
if (PPUON && i==6)
if (PPUON && i == 6)
{
ppu_was_on = true;
}
if (PPUON && i==7)
if (PPUON && i == 7)
{
if (!race_2006)
ppur.increment_hsc();
@ -129,7 +131,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
if ((ppur.ht & 2) != 0) at >>= 2;
at &= 0x03;
at <<= 2;
bgdata.at = at;
bgdata.at = at;
break;
}
case 3:
@ -202,21 +204,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
sprite_eval_write = true;
sprite_zero_go = sprite_zero_in_range;
sprite_zero_in_range = false;
sprite_zero_in_range = false;
yp = sl - 1;
ppuphase = PPUPHASE.BG;
// "If PPUADDR is not less then 8 when rendering starts, the first 8 bytes in OAM are written to from
// the current location of PPUADDR"
if (sl == 0 && PPUON && reg_2003 >= 8 && region==Region.NTSC)
if (sl == 0 && PPUON && reg_2003 >= 8 && region == Region.NTSC)
{
for (int i = 0; i < 8; i++)
{
OAM[i] = OAM[(reg_2003 & 0xF8) + i];
}
}
if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback();
if (PPUViewCallback != null && yp == PPUViewCallback.Scanline) PPUViewCallback.Callback();
@ -271,7 +273,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
sprite_eval_write = false;
}
if (is_even_cycle && oam_index<256)
if (is_even_cycle && oam_index < 256)
{
if ((oam_index + soam_m_index) < 256)
read_value = OAM[oam_index + soam_m_index];
@ -282,14 +284,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{
// if we don't write sprites anymore, just scan through the oam
read_value = soam[0];
oam_index+=4;
oam_index += 4;
}
else if (sprite_eval_write)
{
//look for sprites
if (spr_true_count==0 && soam_index<8)
if (spr_true_count == 0 && soam_index < 8)
{
soam[soam_index*4] = read_value;
soam[soam_index * 4] = read_value;
}
if (soam_index < 8)
@ -312,13 +314,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
spr_true_count++;
if (spr_true_count == 4)
{
oam_index+=4;
oam_index += 4;
soam_index++;
if (soam_index == 8)
{
// oam_index could be pathologically misaligned at this point, so we have to find the next
// nearest actual sprite to work on >8 sprites per scanline option
oam_index_aux = (oam_index%4)*4;
oam_index_aux = (oam_index % 4) * 4;
}
soam_m_index = 0;
@ -327,10 +329,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
else
{
oam_index+=4;
oam_index += 4;
}
}
else if (soam_index>=8)
else if (soam_index >= 8)
{
if (yp >= read_value && yp < read_value + spriteHeight && PPUON)
{
@ -350,7 +352,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
spr_true_count++;
if (spr_true_count == 4)
{
oam_index+=4;
oam_index += 4;
soam_index++;
soam_m_index = 0;
spr_true_count = 0;
@ -358,13 +360,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
else
{
oam_index+=4;
if (soam_index==8)
oam_index += 4;
if (soam_index == 8)
{
soam_m_index++; // glitchy increment
soam_m_index &= 3;
}
}
read_value = soam[0]; //writes change to reads
@ -409,7 +411,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
Reg2002_objoverflow = true;
}
renderbgnow = show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost);
renderbgnow = show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost);
//bg pos is different from raster pos due to its offsetability.
//so adjust for that here
int bgpos = rasterpos + ppur.fh;
@ -448,50 +450,41 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
if (!nes.Settings.DispBackground)
pixelcolor = 0x8000; //whats this? i think its a flag to indicate a hidden background to be used by the canvas filling logic later
//look for a sprite to be drawn
bool havepixel = false;
for (int s = 0; s < soam_index_prev; s++)
//check if the pixel has a sprite in it
if (sl_sprites[1, xt * 8 + xp] != 0 && renderspritenow)
{
int x = t_oam[s].oam_x;
if (rasterpos >= x && rasterpos < x + 8)
int s = sl_sprites[0, xt * 8 + xp];
int spixel = sl_sprites[1, xt * 8 + xp];
int temp_attr = sl_sprites[2, xt * 8 + xp];
//TODO - make sure we dont trigger spritehit if the edges are masked for either BG or OBJ
//spritehit:
//1. is it sprite#0?
//2. is the bg pixel nonzero?
//then, it is spritehit.
Reg2002_objhit |= (sprite_zero_go && s == 0 && pixel != 0 && rasterpos < 255 && show_bg_new && show_obj_new);
//priority handling, if in front of BG:
bool drawsprite = !(((temp_attr & 0x20) != 0) && ((pixel & 3) != 0));
if (drawsprite && nes.Settings.DispSprites)
{
//build the pixel.
//fetch the LSB of the patterns
int spixel = t_oam[s].patterns_0 & 1;
spixel |= (t_oam[s].patterns_1 & 1) << 1;
//shift down the patterns so the next pixel is in the LSB
t_oam[s].patterns_0 >>= 1;
t_oam[s].patterns_1 >>= 1;
//bail out if we already have a pixel from a higher priority sprite.
//notice that we continue looping anyway, so that we can shift down the patterns
//transparent pixel bailout
if (!renderspritenow || havepixel || spixel == 0) continue;
havepixel = true;
//TODO - make sure we dont trigger spritehit if the edges are masked for either BG or OBJ
//spritehit:
//1. is it sprite#0?
//2. is the bg pixel nonzero?
//then, it is spritehit.
Reg2002_objhit |= (sprite_zero_go && s == 0 && pixel != 0 && rasterpos < 255 && show_bg_new && show_obj_new);
//priority handling, if in front of BG:
bool drawsprite = !(((t_oam[s].oam_attr & 0x20) != 0) && ((pixel & 3) != 0));
if (drawsprite && nes.Settings.DispSprites)
{
//bring in the palette bits and palettize
spixel |= (t_oam[s].oam_attr & 3) << 2;
//save it for use in the framebuffer
pixelcolor = PALRAM[0x10 + spixel];
}
} //rasterpos in sprite range
//bring in the palette bits and palettize
spixel |= (temp_attr & 3) << 2;
//save it for use in the framebuffer
pixelcolor = PALRAM[0x10 + spixel];
}
} //oamcount loop
pipeline(pixelcolor, target, xt*32+xp);
target++;
pipeline(pixelcolor, target, xt * 8 + xp);
target++;
// clear out previous sprites from scanline buffer
sl_sprites[0, xt * 8 + xp] = 0;
sl_sprites[1, xt * 8 + xp] = 0;
sl_sprites[2, xt * 8 + xp] = 0;
} //loop across 8 pixels
} //loop across 32 tiles
}
@ -505,7 +498,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
if (nes.Settings.AllowMoreThanEightSprites)
{
while (oam_index_aux < 64 && soam_index_aux<64)
while (oam_index_aux < 64 && soam_index_aux < 64)
{
//look for sprites
soam[soam_index_aux * 4] = OAM[oam_index_aux * 4];
@ -578,12 +571,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
read_value = t_oam[s].oam_y;
runppu(1);
if (PPUON) ppur.install_latches();
read_value = t_oam[s].oam_ind;
runppu(1);
garbage_todo = 0;
}
@ -593,10 +586,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
read_value = t_oam[s].oam_y;
runppu(1);
if (target<=61441 && target > 0 && s==0)
if (target <= 61441 && target > 0 && s == 0)
{
pipeline(0, target,256);
pipeline(0, target, 256);
target++;
}
@ -604,8 +597,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
if (PPUON) ppur.install_h_latches();
read_value = t_oam[s].oam_ind;
runppu(1);
if (target <= 61441 && target > 0 && s==0)
if (target <= 61441 && target > 0 && s == 0)
{
pipeline(0, target, 257); // last pipeline call option 1 of 2
}
@ -615,24 +608,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
for (int i = 0; i < garbage_todo; i++)
{
if (i==0)
if (i == 0)
read_value = t_oam[s].oam_y;
else
read_value = t_oam[s].oam_ind;
runppu(1);
if (i == 0)
{
if (target <= 61441 && target > 0 && s==0)
if (target <= 61441 && target > 0 && s == 0)
{
pipeline(0, target,256);
pipeline(0, target, 256);
target++;
}
}
else
{
if (target <= 61441 && target > 0 && s==0)
if (target <= 61441 && target > 0 && s == 0)
{
pipeline(0, target, 257); // last pipeline call option 2 of 2
}
@ -676,7 +669,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
// if the sprites attribute is 0xFF, then this indicates a non-existent sprite
// I think the logic here is that bits 2-4 in OAM are disabled, but soam is initialized with 0xFF
// so the only way a sprite could have an 0xFF attribute is if it is not in the scope of the scanline
if (t_oam[s].oam_attr==0xFF)
if (t_oam[s].oam_attr == 0xFF)
{
t_oam[s].patterns_0 = 0;
t_oam[s].patterns_1 = 0;
@ -684,10 +677,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
// now that we have a sprite, we can fill in the next scnaline's sprite pixels with it
// this saves quite a bit of processing compared to checking each pixel
if (s < soam_index_prev)
{
int temp_x = t_oam[s].oam_x;
for (int i = 0; (temp_x + i) < 256 && i < 8; i++)
{
if (sl_sprites[1, temp_x + i] == 0)
{
if (t_oam[s].patterns_0.Bit(i) || t_oam[s].patterns_1.Bit(i))
{
int spixel = t_oam[s].patterns_0.Bit(i) ? 1 : 0;
spixel |= (t_oam[s].patterns_1.Bit(i) ? 2 : 0);
sl_sprites[0, temp_x + i] = (byte)s;
sl_sprites[1, temp_x + i] = (byte)spixel;
sl_sprites[2, temp_x + i] = t_oam[s].oam_attr;
}
}
}
}
} // sprite pattern fetch loop
//now do the same for extra sprites, but without any cycles run
if (soam_index_aux>8)
if (soam_index_aux > 8)
{
for (int s = 8; s < soam_index_aux; s++)
{
@ -745,6 +762,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
t_oam[s].patterns_1 = 0;
}
int temp_x = t_oam[s].oam_x;
for (int i = 0; (temp_x + i) < 256 && i < 8; i++)
{
if (sl_sprites[1, temp_x + i] == 0)
{
if (t_oam[s].patterns_0.Bit(i) || t_oam[s].patterns_1.Bit(i))
{
int spixel = t_oam[s].patterns_0.Bit(i) ? 1 : 0;
spixel |= (t_oam[s].patterns_1.Bit(i) ? 2 : 0);
sl_sprites[0, temp_x + i] = (byte)s;
sl_sprites[1, temp_x + i] = (byte)spixel;
sl_sprites[2, temp_x + i] = t_oam[s].oam_attr;
}
}
}
} // sprite pattern fetch loop
}
@ -785,7 +819,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
//should write to those regs during that time, it needs
//to wait for vblank
runppu(241 * kLineTime-3);// -8*3);
runppu(241 * kLineTime - 3);// -8*3);
ppudead--;
}
}