Essgee:指针化优化 DMG音频处理效率

This commit is contained in:
sin365 2025-12-09 21:25:09 +08:00
parent f53fcbfee0
commit b4261139ad
7 changed files with 504 additions and 88 deletions

View File

@ -20,6 +20,12 @@ namespace Essgee.Utilities
GetObjectPtr(srcObj, ref handle, out intptr);
ptr = (uint*)intptr;
}
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref bool* ptr)
{
IntPtr intptr;
GetObjectPtr(srcObj, ref handle, out intptr);
ptr = (bool*)intptr;
}
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref short* ptr)
{

View File

@ -5,14 +5,19 @@
//本身wave就继承了IDMGAudioChannel
public class CGBWave : Wave//, IDMGAudioChannel
{
public override void Reset()
public unsafe override void Reset()
{
base.Reset();
for (var i = 0; i < sampleBuffer.Length; i += 2)
//for (var i = 0; i < sampleBuffer.Length; i += 2)
//{
// sampleBuffer[i + 0] = 0x00;
// sampleBuffer[i + 1] = 0xFF;
//}
for (var i = 0; i < sampleBufferLength; i += 2)
{
sampleBuffer[i + 0] = 0x00;
sampleBuffer[i + 1] = 0xFF;
*(sampleBuffer + i) = 0x00;
*(sampleBuffer + i + 1) = 0xFF;
}
}
}

View File

@ -1,15 +1,36 @@
using System;
using Essgee.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Essgee.Emulation.Audio
{
public partial class DMGAudio
{
public class Noise : IDMGAudioChannel
public unsafe class Noise : IDMGAudioChannel
{
static readonly int[] divisors = new int[]
//static readonly int[] divisors = new int[]
//{
// 8, 16, 32, 48, 64, 80, 96, 112
//};
#region //指针化 divisors
int[] divisors_src;
static GCHandle divisors_handle;
public int* divisors;
public int divisorsLength;
public bool divisors_IsNull => divisors == null;
public int[] divisors_set
{
8, 16, 32, 48, 64, 80, 96, 112
};
set
{
divisors_handle.ReleaseGCHandle();
divisors_src = value;
divisorsLength = value.Length;
divisors_src.GetObjectPtr(ref divisors_handle, ref divisors);
}
}
#endregion
// NR41
byte lengthLoad;
@ -23,7 +44,7 @@ namespace Essgee.Emulation.Audio
bool lfsrWidthMode;
// NR44
bool trigger, lengthEnable;
public bool trigger, lengthEnable;
//
@ -36,8 +57,8 @@ namespace Essgee.Emulation.Audio
bool isEnvelopeUpdateEnabled;
// Misc
bool isChannelEnabled, isDacEnabled;
int lengthCounter;
public bool isChannelEnabled, isDacEnabled;
public int lengthCounter;
//public int OutputVolume { get; private set; }
@ -46,6 +67,12 @@ namespace Essgee.Emulation.Audio
public Noise()
{
//
//初始化一下
divisors_set = new int[]
{
8, 16, 32, 48, 64, 80, 96, 112
};
}
public override void Reset()
@ -106,7 +133,8 @@ namespace Essgee.Emulation.Audio
noiseCounter--;
if (noiseCounter == 0)
{
noiseCounter = divisors[divisorCode] << clockShift;
//noiseCounter = divisors[divisorCode] << clockShift;
noiseCounter = *(divisors + divisorCode) << clockShift;
var result = (lfsr & 0b1) ^ ((lfsr >> 1) & 0b1);
lfsr = (ushort)((lfsr >> 1) | (result << 14));
@ -124,7 +152,8 @@ namespace Essgee.Emulation.Audio
if (lengthCounter == 0) lengthCounter = 64;
noiseCounter = divisors[divisorCode] << clockShift;
//noiseCounter = divisors[divisorCode] << clockShift;
noiseCounter = *(divisors + divisorCode) << clockShift;
volume = envelopeStartingVolume;
envelopeCounter = envelopePeriodReload;
isEnvelopeUpdateEnabled = true;

View File

@ -1,10 +1,12 @@
using System;
using Essgee.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Essgee.Emulation.Audio
{
public partial class DMGAudio
{
public class Square : IDMGAudioChannel
public unsafe class Square : IDMGAudioChannel
{
//static readonly bool[,] dutyCycleTable = new bool[,]
// {
@ -15,45 +17,56 @@ namespace Essgee.Emulation.Audio
//};
// 1. 初始化 - 假设原始数组是 4行 x 8列
private const int Rows = 4;
private const int Cols = 8;
private readonly bool[] _dutyCycleTable1D = new bool[Rows * Cols]
{
// 第一行 (索引 0-7)
false, false, false, false, false, false, false, true,
// 第二行 (索引 8-15)
true, false, false, false, false, false, false, true,
// 第三行 (索引 16-23)
true, false, false, false, false, true, true, true,
// 第四行 (索引 24-31)
false, true, true, true, true, true, true, false
};
public const int Rows = 4;
public const int Cols = 8;
//private readonly bool[] _dutyCycleTable1D = new bool[Rows * Cols]
//{
// // 第一行 (索引 0-7)
// false, false, false, false, false, false, false, true,
// // 第二行 (索引 8-15)
// true, false, false, false, false, false, false, true,
// // 第三行 (索引 16-23)
// true, false, false, false, false, true, true, true,
// // 第四行 (索引 24-31)
// false, true, true, true, true, true, true, false
//};
// 2. 访问方法 - 替代原来的 dutyCycleTable[row, col]
public bool GetValue(int row, int col)
#region //指针化 _dutyCycleTable1D
bool[] _dutyCycleTable1D_src;
GCHandle _dutyCycleTable1D_handle;
public bool* _dutyCycleTable1D;
public int _dutyCycleTable1DLength;
public bool _dutyCycleTable1D_IsNull => _dutyCycleTable1D == null;
public bool[] _dutyCycleTable1D_set
{
// 重要的边界检查(在稳定后可通过条件编译移除以极致优化)
// if (row < 0 || row >= Rows || col < 0 || col >= Cols) return false;
return _dutyCycleTable1D[row * Cols + col];
set
{
_dutyCycleTable1D_handle.ReleaseGCHandle();
_dutyCycleTable1D_src = value;
_dutyCycleTable1DLength = value.Length;
_dutyCycleTable1D_src.GetObjectPtr(ref _dutyCycleTable1D_handle, ref _dutyCycleTable1D);
}
}
#endregion
// NR10/20
byte sweepPeriodReload, sweepShift;
bool sweepNegate;
// NR11/21
byte dutyCycle, lengthLoad;
public byte dutyCycle, lengthLoad;
// NR12/22
byte envelopeStartingVolume, envelopePeriodReload;
bool envelopeAddMode;
// NR13/23
byte frequencyLSB;
public byte frequencyLSB;
// NR14/24
bool trigger, lengthEnable;
byte frequencyMSB;
public bool trigger, lengthEnable;
public byte frequencyMSB;
//
@ -64,15 +77,15 @@ namespace Essgee.Emulation.Audio
int sweepCounter, sweepFreqShadow;
// Frequency
int frequencyCounter;
public int frequencyCounter;
// Envelope
int volume, envelopeCounter;
public int volume, envelopeCounter;
bool isEnvelopeUpdateEnabled;
// Misc
bool isChannelEnabled, isDacEnabled;
int lengthCounter, dutyCounter;
public bool isChannelEnabled, isDacEnabled;
public int lengthCounter, dutyCounter;
//public int OutputVolume { get; private set; }
@ -81,6 +94,19 @@ namespace Essgee.Emulation.Audio
public Square(bool hasSweep)
{
channelSupportsSweep = hasSweep;
//初始化一下
_dutyCycleTable1D_set = new bool[Rows * Cols]
{
// 第一行 (索引 0-7)
false, false, false, false, false, false, false, true,
// 第二行 (索引 8-15)
true, false, false, false, false, false, false, true,
// 第三行 (索引 16-23)
true, false, false, false, false, true, true, true,
// 第四行 (索引 24-31)
false, true, true, true, true, true, true, false
};
}
public override void Reset()
@ -169,7 +195,8 @@ namespace Essgee.Emulation.Audio
//OutputVolume = isDacEnabled && dutyCycleTable[dutyCycle, dutyCounter] ? volume : 0;
//改为一维数组访问
OutputVolume = isDacEnabled && _dutyCycleTable1D[dutyCycle * Cols + dutyCounter] ? volume : 0;
//OutputVolume = isDacEnabled && _dutyCycleTable1D[dutyCycle * Cols + dutyCounter] ? volume : 0;
OutputVolume = isDacEnabled && *(_dutyCycleTable1D+(dutyCycle * Cols + dutyCounter)) ? volume : 0;
}
private void Trigger()

View File

@ -1,8 +1,10 @@
using System;
using Essgee.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Essgee.Emulation.Audio
{
public partial class DMGAudio
public unsafe partial class DMGAudio
{
public class Wave : IDMGAudioChannel
{
@ -19,16 +21,35 @@ namespace Essgee.Emulation.Audio
byte frequencyLSB;
// NR34
bool trigger, lengthEnable;
public bool trigger, lengthEnable;
byte frequencyMSB;
// Wave
protected byte[] sampleBuffer;
//protected byte[] sampleBuffer;
#region //指针化 sampleBuffer
byte[] sampleBuffer_src;
GCHandle sampleBuffer_handle;
public byte* sampleBuffer;
public int sampleBufferLength;
public bool sampleBuffer_IsNull => sampleBuffer == null;
public byte[] sampleBuffer_set
{
set
{
sampleBuffer_handle.ReleaseGCHandle();
sampleBuffer_src = value;
sampleBufferLength = value.Length;
sampleBuffer_src.GetObjectPtr(ref sampleBuffer_handle, ref sampleBuffer);
}
}
#endregion
int frequencyCounter, positionCounter, volume;
// Misc
bool isChannelEnabled;
int lengthCounter;
public bool isChannelEnabled;
public int lengthCounter;
//public int OutputVolume { get; private set; }
@ -36,12 +57,15 @@ namespace Essgee.Emulation.Audio
public Wave()
{
sampleBuffer = new byte[16];
//sampleBuffer = new byte[16];
sampleBuffer_set = new byte[16];
}
public override void Reset()
{
for (var i = 0; i < sampleBuffer.Length; i++) sampleBuffer[i] = (byte)EmuStandInfo.Random.Next(255);
//for (var i = 0; i < sampleBuffer.Length; i++) sampleBuffer[i] = (byte)EmuStandInfo.Random.Next(255);
byte* ptr = sampleBuffer;
for (var i = 0; i < sampleBufferLength; i++, ptr++) *ptr = 0;// (byte)EmuStandInfo.Random.Next(255);
frequencyCounter = positionCounter = 0;
volume = 15;
@ -82,7 +106,8 @@ namespace Essgee.Emulation.Audio
positionCounter++;
positionCounter %= 32;
var value = sampleBuffer[positionCounter / 2];
//var value = sampleBuffer[positionCounter / 2];
var value = *(sampleBuffer + (positionCounter / 2));
if ((positionCounter & 0b1) == 0) value >>= 4;
value &= 0b1111;
@ -168,16 +193,23 @@ namespace Essgee.Emulation.Audio
public override void WriteWaveRam(byte offset, byte value)
{
//if (!isDacEnabled)
// sampleBuffer[offset & (sampleBuffer.Length - 1)] = value;
//else
// sampleBuffer[positionCounter & (sampleBuffer.Length - 1)] = value;
if (!isDacEnabled)
sampleBuffer[offset & (sampleBuffer.Length - 1)] = value;
*(sampleBuffer + (offset & (sampleBufferLength - 1))) = value;
else
sampleBuffer[positionCounter & (sampleBuffer.Length - 1)] = value;
*(sampleBuffer+(positionCounter & (sampleBufferLength - 1))) = value;
}
public override byte ReadWaveRam(byte offset)
{
if (!isDacEnabled)
return sampleBuffer[offset & (sampleBuffer.Length - 1)];
{
//return sampleBuffer[offset & (sampleBufferLength - 1)];
return *(sampleBuffer + (offset & (sampleBufferLength - 1)));
}
else
return 0xFF;
}

View File

@ -19,7 +19,10 @@ namespace Essgee.Emulation.Audio
protected const string channel3OptionName = "AudioEnableCh3Wave";
protected const string channel4OptionName = "AudioEnableCh4Noise";
protected IDMGAudioChannel channel1, channel2, channel3, channel4;
//protected IDMGAudioChannel channel1, channel2, channel3, channel4;
protected Square channel1, channel2;
protected Wave channel3;
protected Noise channel4;
// FF24 - NR50
byte[] volumeRightLeft;
@ -298,6 +301,161 @@ namespace Essgee.Emulation.Audio
//独立声明,不在函数内部
private bool[] channelEnableFlags = new bool[4];
//public void Step(int clockCyclesInStep)
//{
// if (!isSoundHwEnabled) return;
// sampleCycleCount += clockCyclesInStep;
// frameCycleCount += clockCyclesInStep;
// for (int i = 0; i < clockCyclesInStep; i++)
// {
// frameSequencerCounter--;
// if (frameSequencerCounter == 0)
// {
// frameSequencerCounter = frameSequencerReload;
// switch (frameSequencer)
// {
// case 0:
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 1:
// break;
// case 2:
// channel1.SweepClock();
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 3:
// break;
// case 4:
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 5:
// break;
// case 6:
// channel1.SweepClock();
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 7:
// channel1.VolumeEnvelopeClock();
// channel2.VolumeEnvelopeClock();
// channel4.VolumeEnvelopeClock();
// break;
// }
// frameSequencer++;
// if (frameSequencer >= 8)
// frameSequencer = 0;
// }
// //channel1.Step();
// //channel2.Step();
// //channel3.Step();
// //channel4.Step();
// //手动内联
// //channel1.Step();
// if (channel1.isChannelEnabled)
// {
// channel1.frequencyCounter--;
// if (channel1.frequencyCounter == 0)
// {
// channel1.frequencyCounter = (2048 - ((channel1.frequencyMSB << 8) | channel1.frequencyLSB)) * 4;
// channel1.dutyCounter++;
// channel1.dutyCounter %= 8;
// }
// channel1.OutputVolume = channel1.isDacEnabled && *(channel1._dutyCycleTable1D + (channel1.dutyCycle * Square.Cols + channel1.dutyCounter)) ? channel1.volume : 0;
// }
// //channel2.Step();
// if (channel2.isChannelEnabled)
// {
// channel2.frequencyCounter--;
// if (channel2.frequencyCounter == 0)
// {
// channel2.frequencyCounter = (2048 - ((channel2.frequencyMSB << 8) | channel2.frequencyLSB)) * 4;
// channel2.dutyCounter++;
// channel2.dutyCounter %= 8;
// }
// channel2.OutputVolume = channel2.isDacEnabled && *(channel2._dutyCycleTable1D + (channel2.dutyCycle * Square.Cols + channel2.dutyCounter)) ? channel2.volume : 0;
// }
// channel3.Step();
// channel4.Step();
// }
// if (sampleCycleCount >= cyclesPerSample)
// {
// GenerateSample();
// sampleCycleCount -= cyclesPerSample;
// }
// //if (mixedSampleBuffer.Count >= (samplesPerFrame * numOutputChannels))
// if (mixedSampleBuffer_writePos >= (samplesPerFrame * numOutputChannels))
// {
// //EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
// // numChannels,
// // channelSampleBuffer.Select(x => x.ToArray()).ToArray(),
// // new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
// // mixedSampleBuffer.ToArray());
// //有GC
// //EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
// // numChannels,
// // channelSampleBuffer,
// // new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
// // mixedSampleBuffer,
// // mixedSampleBuffer_writePos);
// // 在函数中使用
// channelEnableFlags[0] = !channel1ForceEnable;
// channelEnableFlags[1] = !channel2ForceEnable;
// channelEnableFlags[2] = !channel3ForceEnable;
// channelEnableFlags[3] = !channel4ForceEnable;
// EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
// numChannels,
// channelSampleBuffer,
// channelEnableFlags,
// mixedSampleBuffer,
// mixedSampleBuffer_writePos);
// OnEnqueueSamples(eventArgs);
// FlushSamples();
// eventArgs.Release();
// }
// if (frameCycleCount >= cyclesPerFrame)
// {
// frameCycleCount -= cyclesPerFrame;
// sampleCycleCount = frameCycleCount;
// }
//}
//手动内联
public void Step(int clockCyclesInStep)
{
if (!isSoundHwEnabled) return;
@ -315,10 +473,34 @@ namespace Essgee.Emulation.Audio
switch (frameSequencer)
{
case 0:
channel1.LengthCounterClock();
channel2.LengthCounterClock();
channel3.LengthCounterClock();
channel4.LengthCounterClock();
//channel1.LengthCounterClock();
if (channel1.lengthCounter > 0 && channel1.lengthEnable)
{
channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break;
case 1:
@ -326,20 +508,68 @@ namespace Essgee.Emulation.Audio
case 2:
channel1.SweepClock();
channel1.LengthCounterClock();
channel2.LengthCounterClock();
channel3.LengthCounterClock();
channel4.LengthCounterClock();
//channel1.LengthCounterClock();
if (channel1.lengthCounter > 0 && channel1.lengthEnable)
{
channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break;
case 3:
break;
case 4:
channel1.LengthCounterClock();
channel2.LengthCounterClock();
channel3.LengthCounterClock();
channel4.LengthCounterClock();
//channel1.LengthCounterClock();
if (channel1.lengthCounter > 0 && channel1.lengthEnable)
{
channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break;
case 5:
@ -347,10 +577,34 @@ namespace Essgee.Emulation.Audio
case 6:
channel1.SweepClock();
channel1.LengthCounterClock();
channel2.LengthCounterClock();
channel3.LengthCounterClock();
channel4.LengthCounterClock();
//channel1.LengthCounterClock();
if (channel1.lengthCounter > 0 && channel1.lengthEnable)
{
channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break;
case 7:
@ -365,8 +619,36 @@ namespace Essgee.Emulation.Audio
frameSequencer = 0;
}
channel1.Step();
channel2.Step();
//channel1.Step();
//channel2.Step();
//channel3.Step();
//channel4.Step();
//手动内联
//channel1.Step();
if (channel1.isChannelEnabled)
{
channel1.frequencyCounter--;
if (channel1.frequencyCounter == 0)
{
channel1.frequencyCounter = (2048 - ((channel1.frequencyMSB << 8) | channel1.frequencyLSB)) * 4;
channel1.dutyCounter++;
channel1.dutyCounter %= 8;
}
channel1.OutputVolume = channel1.isDacEnabled && *(channel1._dutyCycleTable1D + (channel1.dutyCycle * Square.Cols + channel1.dutyCounter)) ? channel1.volume : 0;
}
//channel2.Step();
if (channel2.isChannelEnabled)
{
channel2.frequencyCounter--;
if (channel2.frequencyCounter == 0)
{
channel2.frequencyCounter = (2048 - ((channel2.frequencyMSB << 8) | channel2.frequencyLSB)) * 4;
channel2.dutyCounter++;
channel2.dutyCounter %= 8;
}
channel2.OutputVolume = channel2.isDacEnabled && *(channel2._dutyCycleTable1D + (channel2.dutyCycle * Square.Cols + channel2.dutyCounter)) ? channel2.volume : 0;
}
channel3.Step();
channel4.Step();
}

View File

@ -106,13 +106,33 @@ namespace Essgee.Emulation.Video.Nintendo
//取值范例 colorValuesBgr[colorIndex * 3 + channelIndex];
const byte colorValuesBgr_singleLen = 3;
// 转换后的一维数组
readonly byte[] colorValuesBgr = new byte[]
//readonly byte[] colorValuesBgr = new byte[]
//{
///* White */ 0xF8, 0xF8, 0xF8,
///* Light gray */0x9B, 0x9B, 0x9B,
///* Dark gray */ 0x3E, 0x3E, 0x3E,
///* Black */ 0x1F, 0x1F, 0x1F
//};
#region //指针化 colorValuesBgr
byte[] colorValuesBgr_src;
GCHandle colorValuesBgr_handle;
public byte* colorValuesBgr;
public int colorValuesBgrLength;
public bool colorValuesBgr_IsNull => colorValuesBgr == null;
public byte[] colorValuesBgr_set
{
/* White */ 0xF8, 0xF8, 0xF8,
/* Light gray */0x9B, 0x9B, 0x9B,
/* Dark gray */ 0x3E, 0x3E, 0x3E,
/* Black */ 0x1F, 0x1F, 0x1F
};
set
{
colorValuesBgr_handle.ReleaseGCHandle();
colorValuesBgr_src = value;
colorValuesBgrLength = value.Length;
colorValuesBgr_src.GetObjectPtr(ref colorValuesBgr_handle, ref colorValuesBgr);
}
}
#endregion
protected const byte screenUsageEmpty = 0;
protected const byte screenUsageBackground = 1 << 0;
@ -305,6 +325,15 @@ namespace Essgee.Emulation.Video.Nintendo
outputFramebufferCopy_set = new byte[numDisplayPixels * 4];
//初始化一下
colorValuesBgr_set = new byte[]
{
/* White */ 0xF8, 0xF8, 0xF8,
/* Light gray */0x9B, 0x9B, 0x9B,
/* Dark gray */ 0x3E, 0x3E, 0x3E,
/* Black */ 0x1F, 0x1F, 0x1F
};
for (var y = 0; y < displayActiveHeight; y++)
SetLine(y, 0xFF, 0xFF, 0xFF);
}
@ -734,18 +763,24 @@ namespace Essgee.Emulation.Video.Nintendo
//outputFramebuffer[address + 0] = colorValuesBgr[c & 0x03][0];
//outputFramebuffer[address + 1] = colorValuesBgr[c & 0x03][1];
//outputFramebuffer[address + 2] = colorValuesBgr[c & 0x03][2];
outputFramebuffer[address + 0] = colorValuesBgr[(c & 0x03) * 3 + 0];
outputFramebuffer[address + 1] = colorValuesBgr[(c & 0x03) * 3 + 1];
outputFramebuffer[address + 2] = colorValuesBgr[(c & 0x03) * 3 + 2];
outputFramebuffer[address + 3] = 0xFF;
//outputFramebuffer[address + 0] = colorValuesBgr[(c & 0x03) * 3 + 0];
//outputFramebuffer[address + 1] = colorValuesBgr[(c & 0x03) * 3 + 1];
//outputFramebuffer[address + 2] = colorValuesBgr[(c & 0x03) * 3 + 2];
//outputFramebuffer[address + 3] = 0xFF;
*(outputFramebuffer + address) = colorValuesBgr[(c & 0x03) * 3 + 0];
*(outputFramebuffer + address + 1) = colorValuesBgr[(c & 0x03) * 3 + 1];
*(outputFramebuffer + address + 2) = colorValuesBgr[(c & 0x03) * 3 + 2];
*(outputFramebuffer + address + 3) = 0xFF;
}
protected virtual void WriteColorToFramebuffer(byte b, byte g, byte r, int address)
{
outputFramebuffer[address + 0] = b;
outputFramebuffer[address + 1] = g;
outputFramebuffer[address + 2] = r;
outputFramebuffer[address + 3] = 0xFF;
*(outputFramebuffer + address) = b;
*(outputFramebuffer + address + 1) = g;
*(outputFramebuffer + address + 2) = r;
*(outputFramebuffer + address + 3) = 0xFF;
}
protected virtual void ClearScreenUsage()