Skip to content

Commit 389f813

Browse files
committed
Reimplement snowy NullHawk
now with 50% less hacks! merry christmas
1 parent 27e94d9 commit 389f813

File tree

8 files changed

+183
-3
lines changed

8 files changed

+183
-3
lines changed

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.206" />
1212
<PackageVersion Include="Meziantou.Polyfill" Version="1.0.50" />
1313
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
14+
<PackageVersion Include="Microsoft.Bcl.Numerics" Version="10.0.1" />
1415
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
1516
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
1617
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />

src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public DisplayManagerRenderTargetProvider(Func<Size, IRenderTarget> callback)
4949

5050
private IEmulator GlobalEmulator;
5151

52+
public SnowyNullVideo SnowyVP { get; private set; }
53+
5254
protected DisplayManagerBase(
5355
Config config,
5456
IEmulator emulator,
@@ -60,6 +62,10 @@ protected DisplayManagerBase(
6062
{
6163
GlobalConfig = config;
6264
GlobalEmulator = emulator;
65+
SnowyVP = new()
66+
{
67+
LiveSettings = GlobalConfig.GetCoreSettings<NullEmulator, SnowyNullVideo.Settings>() ?? new(),
68+
};
6369
OSD = new(config, emulator, inputManager, movieSession);
6470
_gl = gl;
6571
_renderer = renderer;
@@ -112,6 +118,10 @@ public void UpdateGlobals(Config config, IEmulator emulator)
112118
{
113119
GlobalConfig = config;
114120
GlobalEmulator = emulator;
121+
SnowyVP = new()
122+
{
123+
LiveSettings = GlobalConfig.GetCoreSettings<NullEmulator, SnowyNullVideo.Settings>() ?? new(),
124+
};
115125
OSD.UpdateGlobals(config, emulator);
116126
}
117127

@@ -466,12 +476,12 @@ public Point TransformPoint(Point p)
466476
/// This will receive an emulated output frame from an IVideoProvider and run it through the complete frame processing pipeline
467477
/// Then it will stuff it into the bound PresentationPanel.
468478
/// </summary>
469-
public void UpdateSource(IVideoProvider videoProvider)
479+
public void UpdateSource(IVideoProvider videoProvider, bool useSnow = false)
470480
{
471481
var displayNothing = GlobalConfig.DispSpeedupFeatures == 0;
472482
var job = new JobInfo
473483
{
474-
VideoProvider = videoProvider,
484+
VideoProvider = useSnow ? SnowyVP : videoProvider,
475485
Simulate = displayNothing,
476486
ChainOutsize = GetGraphicsControlSize(),
477487
IncludeOSD = true,

src/BizHawk.Client.Common/config/Config.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,5 +464,7 @@ public void SetWindowScaleFor(string sysID, int windowScale)
464464
public bool ScaleOSDWithSystemScale { get; set; } = true;
465465

466466
public int RelativeMouseSensitivity { get; set; } = 100;
467+
468+
public SnowyNullVideo.TriggerCriterion SnowyNullHawk { get; set; } = SnowyNullVideo.TriggerCriterion.WeekOfChristmas;
467469
}
468470
}

src/BizHawk.Client.EmuHawk/MainForm.Events.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,7 @@ private void DisplayConfigMenuItem_Click(object sender, EventArgs e)
13061306
using DisplayConfig window = new(Config, DialogController, GL);
13071307
if (this.ShowDialogWithTempMute(window).IsOk())
13081308
{
1309+
DisplayManager.UpdateGlobals(Config, Emulator);
13091310
DisplayManager.RefreshUserShader();
13101311
FrameBufferResized();
13111312
SynchChrome();

src/BizHawk.Client.EmuHawk/MainForm.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2348,9 +2348,18 @@ private void SaveSlotSelectedMessage()
23482348
//rendering flakes out egregiously if we have a zero size
23492349
//can we fix it later not to?
23502350
if (isZero)
2351+
{
23512352
DisplayManager.Blank();
2353+
}
23522354
else
2353-
DisplayManager.UpdateSource(video);
2355+
{
2356+
DisplayManager.UpdateSource(video, useSnow: Emulator is NullEmulator && (Config.SnowyNullHawk switch
2357+
{
2358+
SnowyNullVideo.TriggerCriterion.Always => true,
2359+
SnowyNullVideo.TriggerCriterion.WeekOfChristmas => DateTime.Now.DayOfYear is >= 354/*Dec. 20*/ and <= 360/*Dec. 26*/,
2360+
_ => false,
2361+
}));
2362+
}
23542363
}
23552364

23562365
public static readonly FilesystemFilterSet ConfigFileFSFilterSet = new(

src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
using BizHawk.Client.Common;
77
using BizHawk.Client.Common.Filters;
88
using BizHawk.Common;
9+
using BizHawk.Common.NumberExtensions;
10+
using BizHawk.Emulation.Common;
11+
using BizHawk.WinForms.Controls;
912

1013
namespace BizHawk.Client.EmuHawk
1114
{
@@ -21,6 +24,28 @@ public partial class DisplayConfig : Form, IDialogParent
2124

2225
private string _pathSelection;
2326

27+
private readonly RadioButtonGroupTracker _snowRadioTracker;
28+
29+
private readonly SzNUDEx nudSnowBias = new()
30+
{
31+
DecimalPlaces = 2,
32+
Increment = 0.25m,
33+
Maximum = 2.0m,
34+
Minimum = -2.0m,
35+
Size = new(48, 23),
36+
};
37+
38+
private readonly SzNUDEx nudSnowIntensity = new()
39+
{
40+
DecimalPlaces = 1,
41+
Increment = 0.1m,
42+
Maximum = 1.0m,
43+
Minimum = 0.1m,
44+
Size = new(48, 23),
45+
};
46+
47+
private readonly TransparentTrackBar tbSnowFramerate = new() { Maximum = 20, Minimum = 1, Size = new(160, 45) };
48+
2449
public IDialogController DialogController { get; }
2550

2651
public bool NeedReset { get; set; }
@@ -30,8 +55,61 @@ public DisplayConfig(Config config, IDialogController dialogController, IGL gl)
3055
_config = config;
3156
_gl = gl;
3257
DialogController = dialogController;
58+
var snowSettings = _config.GetCoreSettings<NullEmulator, SnowyNullVideo.Settings>() ?? new();
3359

3460
InitializeComponent();
61+
LocSzGroupBoxEx grpSnow = new() { Location = new(6, 200), Size = new(371, 160), Text = "Snowy NullHawk" };
62+
_snowRadioTracker = grpSnow.Tracker;
63+
RadioButtonEx rbSnowAlways = new(_snowRadioTracker)
64+
{
65+
Checked = config.SnowyNullHawk is SnowyNullVideo.TriggerCriterion.Always,
66+
Name = nameof(rbSnowAlways),
67+
Tag = SnowyNullVideo.TriggerCriterion.Always,
68+
Text = "Always",
69+
};
70+
RadioButtonEx rbSnowForChristmas = new(_snowRadioTracker)
71+
{
72+
Checked = config.SnowyNullHawk is SnowyNullVideo.TriggerCriterion.WeekOfChristmas,
73+
Name = nameof(rbSnowForChristmas),
74+
Tag = SnowyNullVideo.TriggerCriterion.WeekOfChristmas,
75+
Text = "During Christmas (Dec. 20th through 26th)",
76+
};
77+
RadioButtonEx rbSnowNever = new(_snowRadioTracker)
78+
{
79+
Checked = config.SnowyNullHawk is SnowyNullVideo.TriggerCriterion.Never,
80+
Name = nameof(rbSnowNever),
81+
Tag = SnowyNullVideo.TriggerCriterion.Never,
82+
Text = "Never",
83+
};
84+
LabelEx lblFramerate = new();
85+
tbSnowFramerate.ValueChanged += (changedSender, _) =>
86+
{
87+
var val = ((TrackBar) changedSender).Value;
88+
lblFramerate.Text = $"Framerate: {60.0 / val:F1} Hz";
89+
};
90+
tbSnowFramerate.Value = snowSettings.FramerateScalar;
91+
nudSnowIntensity.Value = new(snowSettings.Intensity);
92+
nudSnowBias.Value = new(snowSettings.Bias);
93+
grpSnow.Controls.Add(new LocSzSingleColumnFLP
94+
{
95+
Controls =
96+
{
97+
new LabelEx { Text = "When no rom loaded, draw \"snow\" (white noise):" },
98+
new SingleRowFLP { Controls = { rbSnowAlways, rbSnowNever } },
99+
rbSnowForChristmas,
100+
new SingleRowFLP { Controls =
101+
{
102+
new LabelEx { Text = "Brightness multiplier:" },
103+
nudSnowIntensity,
104+
new LabelEx { Text = "RNG bias:" },
105+
nudSnowBias,
106+
} },
107+
new SingleRowFLP { Controls = { lblFramerate, tbSnowFramerate } },
108+
},
109+
Location = new(5, 15),
110+
Size = new(320, 144),
111+
});
112+
tpMisc.Controls.Add(grpSnow);
35113

36114
rbNone.Checked = _config.TargetDisplayFilter == 0;
37115
rbHq2x.Checked = _config.TargetDisplayFilter == 1;
@@ -170,6 +248,15 @@ private void BtnOk_Click(object sender, EventArgs e)
170248

171249
_config.UseStaticWindowTitles = cbStaticWindowTitles.Checked;
172250

251+
_config.SnowyNullHawk = _snowRadioTracker.GetSelectionTagAs<SnowyNullVideo.TriggerCriterion>()
252+
?? SnowyNullVideo.TriggerCriterion.WeekOfChristmas;
253+
_config.PutCoreSettings(
254+
new SnowyNullVideo.Settings(
255+
Bias: nudSnowBias.Value.ConvertToF32(),
256+
FramerateScalar: tbSnowFramerate.Value,
257+
Intensity: nudSnowIntensity.Value.ConvertToF32()),
258+
typeof(NullEmulator));
259+
173260
if (rbUseRaw.Checked)
174261
_config.DispManagerAR = EDispManagerAR.None;
175262
else if (rbUseSystem.Checked)

src/BizHawk.Common/BizHawk.Common.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<ItemGroup>
1111
<PackageReference Include="CommunityToolkit.HighPerformance" />
1212
<PackageReference Include="Microsoft.Bcl.HashCode" />
13+
<PackageReference Include="Microsoft.Bcl.Numerics" />
1314
<PackageReference Include="Microsoft.Win32.Registry" />
1415
<PackageReference Include="PolySharp" />
1516
<PackageReference Include="System.ComponentModel.Annotations" />
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
namespace BizHawk.Emulation.Common
2+
{
3+
/// <seealso cref="NullVideo"/>
4+
public sealed class SnowyNullVideo : IVideoProvider
5+
{
6+
[CoreSettings]
7+
public sealed record class Settings(
8+
float Bias = -2.0f,
9+
int FramerateScalar = 10,
10+
float Intensity = 1.0f);
11+
12+
public enum TriggerCriterion : byte
13+
{
14+
Never = 0,
15+
Always = 1,
16+
WeekOfChristmas = 2,
17+
}
18+
19+
private const int DefaultHeight = 192;
20+
21+
private const int DefaultWidth = 256;
22+
23+
private readonly int[] _buf = new int[DefaultWidth * DefaultHeight];
24+
25+
private int _rep = 0;
26+
27+
private readonly Random _rng = new(Seed: 0x4); // chosen by fair dice roll. guaranteed to be random.
28+
29+
public int BackgroundColor
30+
=> 0;
31+
32+
public int BufferHeight
33+
=> DefaultHeight;
34+
35+
public int BufferWidth
36+
=> DefaultWidth;
37+
38+
public required Settings LiveSettings { get; set; }
39+
40+
public int VirtualHeight
41+
=> DefaultHeight;
42+
43+
public int VirtualWidth
44+
=> DefaultWidth;
45+
46+
public int VsyncDenominator
47+
=> LiveSettings.FramerateScalar;
48+
49+
public int VsyncNumerator
50+
=> 60;
51+
52+
public int[] GetVideoBuffer()
53+
{
54+
if (_rep++ > LiveSettings.FramerateScalar) _rep = 0;
55+
if (_rep is not 0) return _buf;
56+
var noise = new byte[_buf.Length];
57+
_rng.NextBytes(noise);
58+
for (var i = 0; i < _buf.Length; i++)
59+
{
60+
var sampleF = noise[i] / 255.0f;
61+
sampleF = MathF.Pow(sampleF, MathF.Pow(10.0f, -LiveSettings.Bias)) * LiveSettings.Intensity;
62+
var sample = (byte) (sampleF * 255.0f);
63+
const int MASK = ~0xFFFFFF;
64+
_buf[i] = MASK | sample << 16 | sample << 8 | sample; // ARGB
65+
}
66+
return _buf;
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)