So I'm working on a game boy emulator and using SFML to handle the graphics.
My program uses 2 thread: one to run the emulator and another to handle the view port window.
The main thread goes through these steps:
1. Spawn a new thread that handles the viewport window (the window gets a 60fps limit)
2. Waits for user input at the console before starting execution of the game rom by the emulator core
3. Waits for the emulator to finish rendering a frame buffer
4. Sends the frame buffer to the window thread to be display (this step blocks on a mutex thereby meeting the 60fps limit)
5. Repeats from step 3
The window thread is best explained with the code. RunUntilQuitEvent is the entry point for its thread.
void VpWindow::ApplyFrameBuffer()
{
int size = buffer_width * buffer_height;
for (int i = 0; i < size; i++)
{
int index = i * 4;
sf_frame_buffer[index + 0] = em_frame_buffer[i].Red * 8;
sf_frame_buffer[index + 1] = em_frame_buffer[i].Green * 8;
sf_frame_buffer[index + 2] = em_frame_buffer[i].Blue * 8;
sf_frame_buffer[index + 3] = 0xFF;
}
window->clear();
texture->update(sf_frame_buffer);
window->draw(*rectangle, *transform);
window->display();
}
void VpWindow::RunUntilQuitEvent()
{
while (window->isOpen())
{
Event event;
while (window->pollEvent(event))
{
if (event.type == Event::Closed)
{
window->close();
}
else if (event.type == Event::KeyPressed && HandlesKeyPress)
{
KeyPressHandler(event);
}
else if (event.type == Event::KeyReleased && HandlesKeyRelease)
{
KeyReleaseHandler(event);
}
}
// Since a window can only be updated from the thread that created it, we can't call ApplyFrameBuffer() from the main thread
// because the main thread spawns a separate thread for each window before constructing them.
// The main thread also holds the frame buffer, so to update the window from there it calls the SetFrameBuffer()
// function which sets the frame_buffer member to a non-null value which signals to this thread that it is ready to be drawn.
buffer_lock.lock();
if (em_frame_buffer != nullptr)
{
ApplyFrameBuffer();
em_frame_buffer = nullptr;
}
buffer_lock.unlock();
}
}
The problem is I'm getting a pretty high CPU usage (~30%) even before step 3 in the main thread (i.e. before the main thread begins the emulation).
I profiled the program. During the phase before the emulation begins 98% of the CPU time for the window thread is spent on this line: while (window->pollEvent(event))
Once emulation begins, CPU usage drops to around 5%, which is still a little high but more manageable. This time the "while (window->pollEvent(event))" line is using 18% of the window thread's time.
Looking through some other examples, I believe my problem is my window thread should look like this:
while (window->isOpen())
{
Event event;
while (window->pollEvent(event)) { ... }
window->clear(); // optional
window->draw(...);
window->display(...);
}
Instead it looks more like this:
while (window->isOpen())
{
Event event;
while (window->pollEvent(event)) { ... }
if (<false before emulation begins and true sometimes after it begins)
{
window->clear(); // optional
window->draw(...);
window->display(...);
}
}
So what is the proper way to share a frame buffer between 2 threads? And what do I do in the window thread before a frame buffer is ready to avoid spinning too much?
Basically, I'm asking what I can do to minimize CPU usage by the window thread.
Any help appreciated