Skip to content

SDL Example: Assertion io.DeltaTime >= 0 in examples when FPS is very high #996

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Wizzard033 opened this issue Jan 26, 2017 · 20 comments
Closed
Labels

Comments

@Wizzard033
Copy link

Wizzard033 commented Jan 26, 2017

The following line in imgui_impl_sdl_gl3.cpp fails very often

io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);

It fails due to the assertion io.DeltaTime >= 0 which isn't true here
I see this same line in a lot of the examples, and I don't know how it's managed to get this far
Maybe it is just a MinGW thing that causes this to be negative at very high framerates (5000+)

My fix I did follows

io.DeltaTime = g_Time > 0.0 ? std::max(0.f, (float)(current_time - g_Time)) : (float)(1.0f / 60.0f);
@ocornut
Copy link
Owner

ocornut commented Jan 28, 2017

Your suggested fix is incorrect at it will create spikes of delta time. Those times are used for various time measurement (mostly the double click and various repeat speed).

You should investigate why you are getting a negative DeltaTime in the first place.

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 28, 2017

I just compile the example and get this issue. I believe its due to hardware and/or MinGW.
It only happens when the FPS is greater than about 5000.

Windows 10
Intel Core i5-6500
MinGW GCC 5.3.0
ImGui 1.49 release

How do I remedy this in a good way using code?

@Wizzard033
Copy link
Author

Okay! I tested a new way to fix it and it worked. Let me know if it's better.

// Setup time step
Uint32 current_time = SDL_GetTicks();
io.DeltaTime = g_Time > 0 ? (float)((current_time - g_Time) / 1000.0f) : (float)(1.0f / 60.0f);
g_Time = current_time;

I had to change the type of g_Time to Uint32 near the top of imgui_impl_sdl_gl3.cpp too.

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 28, 2017

I'd also like to mention that the example is constantly spamming the assertion when ran.
It's not some rare thing for me that just happens sometimes while running it.

@ocornut
Copy link
Owner

ocornut commented Jan 28, 2017

Could you log, say, the 1000 first values returned by SDL_GetTicks() in that case and then dumping them out?

Something like:

static Uint32 buf[1000];
buf_n = 0;
Uint32 current_time = SDL_GetTicks();
if (buf_n < 1000) 
  buf[buf_n] = current_time;
buf_n++;
if (buf_n == 1000)
{
  FILE* f = fopen("log.txt", "wt");
  for (int n = 0; n < 1000; n++) 
    fprintf(f, "[%04d] %u\n", n, buf[n]);
  fclose(f);
}

@Wizzard033
Copy link
Author

log.txt

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 29, 2017

Here's another log I did using the following code

static double buf[1000];
static int buf_n = 0;
double time = SDL_GetTicks() / 1000.0;
if (buf_n < 1000)
    buf[buf_n] = time;
buf_n++;
if (buf_n == 1000) {
    FILE* f = fopen("log.txt", "wt");
    for (int n = 1; n < 1000; n++) {
        float result = (float)(buf[n] - buf[n - 1]);
        fprintf(f, "[%04d] prev = %04f, curr = %04f, (curr - prev) = %04f, negative = %s\n", n, buf[n - 1], buf[n], result, result < 0 ? "true" : "false");
    }
    fclose(f);
}

log-exception.txt

During the log, I had to click "Ignore" on many, many exceptions...
All the exceptions were from the original example code being ran, and not the above code.
It doesn't actually show anything useful, but I wanted to investigate...

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 29, 2017

One more test I did that makes sure the log is even more representative
It does this by interspersing with the actual time code and using an assert that never failed

// Setup time step
static double buf[1000];
static int buf_n = 0;
Uint32	time = SDL_GetTicks();
double current_time = time / 1000.0;
if (buf_n < 1000) {
    buf[buf_n] = current_time;
    assert((buf_n == 0) || (g_Time == buf[buf_n - 1]));
}
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);
g_Time = current_time;
buf_n++;
if (buf_n == 1000) {
    FILE* f = fopen("log.txt", "wt");
    for (int n = 1; n < 1000; n++) {
        float result = (float)(buf[n] - buf[n - 1]);
        fprintf(f, "[%04d] prev = %04f, curr = %04f, (curr - prev) = %04f, negative = %s\n", n, buf[n - 1], buf[n], result, result < 0 ? "true" : "false");
    }
    fclose(f);
}

log-exception2.txt

Once again I had to click "Ignore" on many, many exceptions, but all for io.DeltaTime.
Again, this tests seems to have no negative results...

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 29, 2017

The assert actually failed in the previous test. I forgot to compile in debug mode.

static double       g_Time = 0;

I have no idea why that assert would fail, but I could just be missing something trivial here.

EDIT: I fixed it in the above post.

I also found that my compiler optimization flag doesn't seem to matter when compiling the example.

@Wizzard033
Copy link
Author

It seems as if my fix would work if the library allowed io.DeltaTime = 0 (see the referenced issue)

@ocornut
Copy link
Owner

ocornut commented Jan 31, 2017

I haven't have time to look at it.

Allowing io.DeltaTime = 0.0f makes little sense and even if some workaround would be possible in term of handling buttons, other things would break.

Make sure your app doesn't give DeltaTime 0.0 (the sdl2 example code probably needs fixing as you suggested above, but passing 0.0f isn't a correct fix).

@ocornut
Copy link
Owner

ocornut commented Jan 31, 2017

Replying here instead of the other closed thread.

It's incorrect to use a millisecond precision timer and have greater than 1000 FPS with your library?

It's incorrect to give a zero DeltaTime.

If your application wants to deal with 1000+ FPS framerate then surely your application shouldn't be using a millisecond precision timer? If you can get a precise enough value at least uses 1.0f/FPS instead of zero..

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 31, 2017

Okay. That makes sense I guess. I'll go ahead and limit FPS at 1000 for my application. Thanks!
I think there should be some note made of this in the documentation so others don't waste hours like me.

The original issue still applies, and fixing the example is more involved as it needs to have it's FPS capped.
I never did figure out exactly why the assertion triggers, but storing g_Time as Uint32 makes it go away.

@ocornut
Copy link
Owner

ocornut commented Jan 31, 2017

Yes there's probably an issue with that SDL2 example, I will look at it.
You don't need to cap your app to 1000 PFS to instead of passing 0.0f as a fallback pass 1.0f/FPS as a vague estimation of time passing.

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 31, 2017

Good suggestion. Thanks!

That does make the timing less accurate, but I guess it would go unnoticed.

@ocornut ocornut changed the title Assertion io.DeltaTime >= 0 in examples when FPS is very high SDL Example: Assertion io.DeltaTime >= 0 in examples when FPS is very high Jan 31, 2017
@MrSapps
Copy link

MrSapps commented Jan 31, 2017

@Wizzard033
Copy link
Author

Wizzard033 commented Jan 31, 2017

Does ImGui use cumulative time for anything? If so, nanoseconds is probably too much for a float.
This is why I didn't go that route, but without knowing how ImGui works, I can't tell.

EDIT: oh, i guess we could just cast to seconds though. whoops!

@MrSapps
Copy link

MrSapps commented Jan 31, 2017

If you up convert the units it still won't be zero..

@AlfonsoJLuna
Copy link

SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency() can be used for a more precise timer.
This could be a solution:

// Setup time step
double current_time = SDL_GetPerformanceCounter() / SDL_GetPerformanceFrequency();
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f);
g_Time = current_time;

ocornut added a commit that referenced this issue Feb 5, 2018
…Frequency() to handle framerate over 1000 fps properly. Noticed bad inputs artefacts in Nav branch at 2000 fps without this. (#996)
@ocornut
Copy link
Owner

ocornut commented Feb 5, 2018

@Wizzard033 I finally got around to fix this (using the performance counter as suggested by @AlfonsoJLuna).

My apologies Brandon for taking so long to tackle such a simple problem. Today I was testing Navigation branch input and the problem was very noticeable due to how the Nav branch handle its inputs (it's currently not tolerant to io.DeltaTime being 0.0f)

Note that I also changed the SDL example to enable vsync so they'll probably be capped to 60 hz etc. by default but I tested it for high framerates.

@ocornut ocornut closed this as completed Feb 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants