I'm making Nes emulator in C# and WPF. My problem, basically is how to move emulation work on another thread while still seeing screen updates it makes on window i UI thread. I'm currently set up like this.
MainWindow.xaml
<Image x:Name="NesScreen" Stretch="Fill" Height="720" Width="768"/>
First, naive, solution I tried was:
MainWindow.xaml.cs
namespace NesEmulatorGUI
{
public partial class MainWindow : Window
{
private Nes nes;
public MainWindow()
{
InitializeComponent();
nes = new Nes();
CompositionTarget.Rendering += RenderNesScreen;
}
protected void RenderNesScreen(object sender, EventArgs e)
{
// ... handle input
//Stopwatch stopwatch = Stopwatch.StartNew();
do
{
nes.Clock();
}
while (!nes.FrameComplete);
//stopwatch.Stop();
NesScreen.Source = nes.Screen;
nes.FrameComplete = false;
//long timeTaken = stopwatch.ElapsedMilliseconds;
//int timeToSleep = Math.Max((int)(1000.0 / 60 - timeTaken), 0);
//Thread.Sleep(timeToSleep);
}
}
}
nes.Screen
is WriteableBitmap
containing current frame.
This turned out to produce sluggish experience (especially in Debug build) as Nes emulation is blocking main thread. Window controls are irresponve and moving windows itself is sluggish.
Second problem with this approach is in commented lines. I don't have any control to render in 60fps, as I would block main thread even further if I uncommented those lines.
According to all above, I think that moving emulation to another thread is best approach here. But I faced some issues with all approaches I took:
- moving Nes clock to
BackgroundWorker
- creating thread to run Nes clocks
nesThread = new Thread(new ThreadStart(RunNes));
nesThread.IsBackground = true;
nesThread.Start();
But with all these I tend to get the same issue. In Nes thread I get this error
System.InvalidOperationException: 'The calling thread cannot access this object because a different thread owns it.'
on line in Nes implementation where I write to WriteableBitmap
, once frame is finished.
I think it is because I've set NesScreen
source to nes.Screen
once and Nes thread doesn't have access to that object anymore. I tried locking NesScreen
before altering its source but that seems to result in the same issue.
How can I best organize Nes emulation and rendering into threads? Another thing I have to keep in mind is that I want Nes to produce new frames in 60fps and for that I would need either some kind of scheduling of single frame emulation call or thread sleeping like in commented lines above.
Thanks in advance