Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - dassie

Pages: [1]
1
Audio / Proper usage of SoundStream class
« on: March 30, 2020, 01:24:00 am »
I am using the SoundStream class to output computed audio and I'm unsure if I'm using it correctly because I notice that the onGetData() function gets called kind of "irregularly".

My implementation is pretty simple. In my class constructor I call initialize(2,44100). From the SFML docs I gathered that the audio format is signed 16 bit integers but there's no mention of how to encode channels, I assumed they need to be output back-to-back (e.g. the stream of Int16s looks like: L0,R0,L1,R1,L2,R2,...). I'm also using version 2.5.1.

onGetData() looks like this:
bool MySoundStream::onGetData(Chunk& chunk)
{
   // buffer is a vector<Int16>

   buffer.clear();
  ReadAudioStream(buffer); // <- This function waits for a lock to access the compute thread's data

   chunk.samples = buffer.data();
   chunk.sampleCount = buffer.size();

   LOG_INFO("Sample count = %d", chunk.sampleCount);

   return chunk.sampleCount > 0;
}


When I notice that onGetData() gets called very rapidly about ~1ms apart, and the second one ends up fetching nothing:

[23:06:58.472] [WARN] MySoundStream::onGetDataSample count = 41300
[23:06:58.472] [WARN] MySoundStream::onGetDataSample count = 0
The thread 0x63ac has exited with code 0 (0x0).
[23:06:58.958] [WARN] MySoundStream::onGetDataSample count = 41300
[23:06:58.958] [WARN] MySoundStream::onGetDataSample count = 0
The thread 0x3eb4 has exited with code 0 (0x0).
[23:06:59.445] [WARN] MySoundStream::onGetDataSample count = 42776
[23:06:59.446] [WARN] MySoundStream::onGetDataSample count = 0
The thread 0x1184 has exited with code 0 (0x0).
[23:06:59.946] [WARN] MySoundStream::onGetDataSample count = 42774
[23:06:59.946] [WARN] MySoundStream::onGetDataSample count = 0
The thread 0x954 has exited with code 0 (0x0).
[23:07:00.445] [WARN] MySoundStream::onGetDataSample count = 41300
[23:07:00.445] [WARN] MySoundStream::onGetDataSample count = 0


Another issue I noticed is _sometimes_ the audio playback stalls a lot. So the logs end up showing large chunks of samples being fetched as it catches up (the problem of reading 0 samples doesn't show up, each consecutive call as it tries to catch up will have data in it). The playback doesn't corrects itself unless I call stop() then play() again.

Sorry for the incomplete code example I'm giving (I can clarify more if needed), I'm moreso asking to confirm that my assumptions about the usage is correct and to find out why onGetData() is being called 1ms apart like that and sometimes not being called for long gaps.

Edit: after checking the source code a bit it looks like this is because the base class is assuming my audio stream is finished when I return false (from looking at SoundStream::fillAndPushBuffer()). I will change up my code to make sure it always returns some data.

2
General / Re: How to lower CPU usage
« on: September 09, 2017, 01:53:00 am »
Thanks for the response.
You're welcome.

2 reasons I want to keep them in separate threads. The main thread is going to handle user-input from the console without blocking the viewport. And I also plan to add another window into the mix that runs parallel to the view port.
Why is the main "input" thread blocking the viewport? Is your input blocking? If not, you can update your input inside the window update loop as 'normal'. If it is blocking and you insist on keeping this, why not place the input in its own thread instead? This allows the window(s) to be fully controlled by the main thread and the input thread doesn't "spin".

It was also just easier for me to wrap my head around the problem if I could I keep them in separate threads.
Adding threads when they're not necessary almost always makes things less simple than without them...

Regardless, if it's possible to do it in multiple threads, then that's the problem I want to solve.
Why? A lot of things are possible but it doesn't mean that they should be done ;D

It's only 2 threads sharing 1 resource. It's not that complicated.

I don't want to give up and change my design. I'm sure I can figure it out; I just wanted to see if anyone knew how I could fix this multithread situation.

3
General / Re: How to lower CPU usage
« on: September 09, 2017, 12:52:14 am »
If you're using the same object in multiple threads, the simplest way to help make them thread safe is to use a mutex every time you want to access it.
Consider setting your window to use vertical synchronization or setting a limit for its framerate. (note: not both)

It's generally recommended that you create and control a window (and its events) from the main thread.


EDIT: More thorough information posted above by eXpl0it3r.

Thanks for the response. I am actually using a mutex to share the data and limiting the framerate already.

4
General / Re: How to lower CPU usage
« on: September 09, 2017, 12:48:41 am »
To me it sounds like your second thread is pretty much useless, since your main thread simply waits for the second thread to do its job, so you have nothing that really runs in parallel.
How long does an emulation frame take to render? Unless it's closer to 30s, there's really not point in having one thread just for handling events.
Asked differently, what do you expect to gain from having the emulation in a separate thread?

As for the problem, ask yourself: How does a CPU get a high usage?
Answer: When a thread is doing processing none stop. When you just have one loop that runs as fast as it can, you're CPU usage will jump up even though you're not really doing any work.

while(window.isOpen()) { // Iterates as fast as possible
    sf::Event event;
    while(window.pollEvent(event)) { // Iterates as fast as possible
   
    }
   
    if(false) {
        // clear, draw, display
    }
}

Things to prevent this are to put the thread to sleep for a certain amount of time.
If you let the window draw, regardless if the buffer is ready or not, then you could use setFramerateLimit or activate VSync. Which then will sleep the thread in the display function (as long as your graphics driver implements VSync in a useful way).

2 reasons I want to keep them in separate threads. The main thread is going to handle user-input from the console without blocking the viewport. And I also plan to add another window into the mix that runs parallel to the view port. It was also just easier for me to wrap my head around the problem if I could I keep them in separate threads.

Regardless, if it's possible to do it in multiple threads, then that's the problem I want to solve.

Anyways, I tried changed that snippet in my while-window-open loop to this. The CPU usage went way down. <1% before emulation. And <3% after. But the framerate is not inconsistent (speeds up and slows down randomly).
                buffer_lock.lock();
                if (em_frame_buffer != nullptr)
                {
                        ApplyFrameBuffer();
                        em_frame_buffer = nullptr;
                }
                else
                {
                        window->clear();
                        window->draw(*rectangle, *transform);
                        window->display();
                }
                buffer_lock.unlock();

5
General / How to lower CPU usage
« on: September 08, 2017, 11:53:42 pm »
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 :)

6
General / Re: Visual Studio EXE + Resources
« on: September 03, 2017, 08:45:12 am »
Thought this was from august, not april. He's long gone by now haha

7
General / Re: Visual Studio EXE + Resources
« on: September 03, 2017, 08:43:44 am »
You need to link against the statically linkable libraries (*-s.lib) - the docs are hella useful.

8
General / Re: Creating DPI aware programs
« on: September 03, 2017, 07:38:27 am »
Yeah, that'd work.
However, that requires you to re-calculate the scale of the buffer everytime the window size changes.
A view, though, wouldn't need updating; it's automatic, saves duplicating work and effectively does the same thing (transforms all positions when drawing).

I didn't know that. For now I'm making the window non-resizeable (even if the DPI changes) - I'm planning to make it resizeable at some point in the future so I'll keep that in mind for that happens.

9
General / Re: Colour interpolation during scaling
« on: September 03, 2017, 02:28:08 am »
Of course! Thank you very much, it works now :)

10
General / Re: Colour interpolation during scaling
« on: September 02, 2017, 10:10:00 pm »
Cool, so I went with that first thing you mentioned. It works :)

However, it's a bit round-about to put all my data into the image, then update the texture from the image. I did a CPU profile and a significant amount of my "UpdateWindow" function was being spent on the Image::setPixel() function.

I tried a different approach to get a little more speed out of it. I maintain a Uint8*, update it with my emulator's buffer, then call texture::update before drawing it to the screen

 
        for (int i = 0; i < width; i++)
        {
                for (int j = 0; j < height; j++)
                {    
                        // sf_frame_buffer is the Uint8*
                        int index = i * width + j * 4;
                        sf_frame_buffer[index + 0] = em_frame_buffer[i][j].Red;
                        sf_frame_buffer[index + 1] = em_frame_buffer[i][j].Green;
                        sf_frame_buffer[index + 2] = em_frame_buffer[i][j].Blue;
                        sf_frame_buffer[index + 3] = 0xFF;
                }
        }

        window->clear();
        texture->update(sf_frame_buffer);
        window->draw(*rectangle, *transform);
        window->display();

Hope that makes sense, what I'm trying to do.

The result ends up looking really wonky. The image looks like its squeezed vertically.
http://i.imgur.com/PJUWUdU.png

I don't see anything wrong with the code. Here's the part where I initialize all my objects:
Code: [Select]
VideoMode vm(width * integer_scale, height * integer_scale);
window = new RenderWindow(vm, title);
window->clear(Color::White);
window->display();

transform = new Transform();
transform->scale(scale_factor, scale_factor);

rectangle = new RectangleShape(Vector2f(width, height));
texture = new Texture();
texture->create(width, height);
rectangle->setTexture(texture);

// Each pixel has an 8bit R, G, B and A value. Hence 4 times the number of pixels.
sf_frame_buffer = new Uint8[4 * width * height];

Any idea what I'm doing wrong?

11
General / Colour interpolation during scaling
« on: September 02, 2017, 09:13:04 am »
Apologies if this comes off as spamming. I asked my question in this thread, but the title is unrelated so it would be missed.
https://en.sfml-dev.org/forums/index.php?topic=22453.0

The issue I'm having is that I use a scaling transform when drawing a vertex array (of points) to a window. Like so:

   window->clear();
   window->draw(*varr, *transform);
   window->display();

The clear() fills the window with black, then the draw() call "stretches" my vertex array without interpolating the colours in between. So what's supposed to be a white background ends up looking grey (i.e. spaced apart white pixels overtop of a black background).

Screenshot:
http://i.imgur.com/J7z9k1r.png

Is there a way to tell the transform to interpolate the colors in between? In some examples related to sf::Transform, the scaling does interpolate colors. Is it due to the fact that I'm using PrimitiveType::Points and this is why it can't do the interpolation?

12
General / Re: Creating DPI aware programs
« on: September 02, 2017, 04:32:45 am »
Thank for the response, I actually already went ahead and coded something up.

You're right, I had to set the window size my self based on the scale factor.

To scale the emulation frame buffer, I opted to use a sf::Transform rather than bitmap scaling myself by drawing more pixels. Basically, I copy my emulator's frame buffer to a VertexArray of points, then I draw it to the window using an scale transform.


13
General / Creating DPI aware programs
« on: September 01, 2017, 02:25:36 am »
So it seems that SFML does not adjust window sizes and such based on the system's DPI settings.

I'm looking for a strategy wherein I can make my SFML program DPI-aware myself (a game boy emulator). I'm able to calculate the scaling factor that is required and now I'm wondering how to account for it.

I could so something as simple as scaling my window size and number of screen pixels per emulator pixel by the same scaling factor. E.g. if the factor is 2, then I'll double the window dimensions, and draw 4 pixels for everyone 1 pixel computed by the emulator.

But I also saw a post where someone suggested using a view and setting the zoom setting on the view object. Is that more efficient? Can the view also scale window dimensions?

Pages: [1]