-
-
Notifications
You must be signed in to change notification settings - Fork 11k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The FPS counter was called only on new frames, so it could not print values regularly, especially when there are very few FPS (when the device surface does not change). To the extreme, it was never able to display 0 fps. Add a separate thread to print framerate every second.
- Loading branch information
Showing
6 changed files
with
206 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,169 @@ | ||
#include "fps_counter.h" | ||
|
||
#include <SDL2/SDL_assert.h> | ||
#include <SDL2/SDL_timer.h> | ||
|
||
#include "lock_util.h" | ||
#include "log.h" | ||
|
||
void | ||
#define FPS_COUNTER_INTERVAL_MS 1000 | ||
|
||
bool | ||
fps_counter_init(struct fps_counter *counter) { | ||
counter->started = false; | ||
// no need to initialize the other fields, they are meaningful only when | ||
// started is true | ||
} | ||
counter->mutex = SDL_CreateMutex(); | ||
if (!counter->mutex) { | ||
return false; | ||
} | ||
|
||
void | ||
fps_counter_start(struct fps_counter *counter) { | ||
counter->started = true; | ||
counter->slice_start = SDL_GetTicks(); | ||
counter->nr_rendered = 0; | ||
counter->nr_skipped = 0; | ||
counter->state_cond = SDL_CreateCond(); | ||
if (!counter->state_cond) { | ||
SDL_DestroyMutex(counter->mutex); | ||
return false; | ||
} | ||
|
||
counter->thread = NULL; | ||
SDL_AtomicSet(&counter->started, 0); | ||
// no need to initialize the other fields, they are unused until started | ||
|
||
return true; | ||
} | ||
|
||
void | ||
fps_counter_stop(struct fps_counter *counter) { | ||
counter->started = false; | ||
fps_counter_destroy(struct fps_counter *counter) { | ||
SDL_DestroyCond(counter->state_cond); | ||
SDL_DestroyMutex(counter->mutex); | ||
} | ||
|
||
// must be called with mutex locked | ||
static void | ||
display_fps(struct fps_counter *counter) { | ||
unsigned rendered_per_second = | ||
counter->nr_rendered * 1000 / FPS_COUNTER_INTERVAL_MS; | ||
if (counter->nr_skipped) { | ||
LOGI("%d fps (+%d frames skipped)", counter->nr_rendered, | ||
LOGI("%u fps (+%u frames skipped)", rendered_per_second, | ||
counter->nr_skipped); | ||
} else { | ||
LOGI("%d fps", counter->nr_rendered); | ||
LOGI("%u fps", rendered_per_second); | ||
} | ||
} | ||
|
||
// must be called with mutex locked | ||
static void | ||
check_expired(struct fps_counter *counter) { | ||
uint32_t now = SDL_GetTicks(); | ||
if (now - counter->slice_start >= 1000) { | ||
display_fps(counter); | ||
// add a multiple of one second | ||
uint32_t elapsed_slices = (now - counter->slice_start) / 1000; | ||
counter->slice_start += 1000 * elapsed_slices; | ||
counter->nr_rendered = 0; | ||
counter->nr_skipped = 0; | ||
check_interval_expired(struct fps_counter *counter, uint32_t now) { | ||
if (now < counter->next_timestamp) { | ||
return; | ||
} | ||
|
||
display_fps(counter); | ||
counter->nr_rendered = 0; | ||
counter->nr_skipped = 0; | ||
// add a multiple of the interval | ||
uint32_t elapsed_slices = | ||
(now - counter->next_timestamp) / FPS_COUNTER_INTERVAL_MS + 1; | ||
counter->next_timestamp += FPS_COUNTER_INTERVAL_MS * elapsed_slices; | ||
} | ||
|
||
static int | ||
run_fps_counter(void *data) { | ||
struct fps_counter *counter = data; | ||
|
||
mutex_lock(counter->mutex); | ||
while (!counter->interrupted) { | ||
while (!counter->interrupted && !SDL_AtomicGet(&counter->started)) { | ||
cond_wait(counter->state_cond, counter->mutex); | ||
} | ||
while (!counter->interrupted && SDL_AtomicGet(&counter->started)) { | ||
uint32_t now = SDL_GetTicks(); | ||
check_interval_expired(counter, now); | ||
|
||
SDL_assert(counter->next_timestamp > now); | ||
uint32_t remaining = counter->next_timestamp - now; | ||
|
||
// ignore the reason (timeout or signaled), we just loop anyway | ||
cond_wait_timeout(counter->state_cond, counter->mutex, remaining); | ||
} | ||
} | ||
mutex_unlock(counter->mutex); | ||
return 0; | ||
} | ||
|
||
bool | ||
fps_counter_start(struct fps_counter *counter) { | ||
mutex_lock(counter->mutex); | ||
counter->next_timestamp = SDL_GetTicks() + FPS_COUNTER_INTERVAL_MS; | ||
counter->nr_rendered = 0; | ||
counter->nr_skipped = 0; | ||
mutex_unlock(counter->mutex); | ||
|
||
SDL_AtomicSet(&counter->started, 1); | ||
cond_signal(counter->state_cond); | ||
|
||
// counter->thread is always accessed from the same thread, no need to lock | ||
if (!counter->thread) { | ||
counter->thread = | ||
SDL_CreateThread(run_fps_counter, "fps counter", counter); | ||
if (!counter->thread) { | ||
LOGE("Could not start FPS counter thread"); | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void | ||
fps_counter_stop(struct fps_counter *counter) { | ||
SDL_AtomicSet(&counter->started, 0); | ||
cond_signal(counter->state_cond); | ||
} | ||
|
||
bool | ||
fps_counter_is_started(struct fps_counter *counter) { | ||
return SDL_AtomicGet(&counter->started); | ||
} | ||
|
||
void | ||
fps_counter_interrupt(struct fps_counter *counter) { | ||
if (!counter->thread) { | ||
return; | ||
} | ||
|
||
mutex_lock(counter->mutex); | ||
counter->interrupted = true; | ||
mutex_unlock(counter->mutex); | ||
// wake up blocking wait | ||
cond_signal(counter->state_cond); | ||
} | ||
|
||
void | ||
fps_counter_join(struct fps_counter *counter) { | ||
if (counter->thread) { | ||
SDL_WaitThread(counter->thread, NULL); | ||
} | ||
} | ||
|
||
void | ||
fps_counter_add_rendered_frame(struct fps_counter *counter) { | ||
check_expired(counter); | ||
if (!SDL_AtomicGet(&counter->started)) { | ||
return; | ||
} | ||
|
||
mutex_lock(counter->mutex); | ||
uint32_t now = SDL_GetTicks(); | ||
check_interval_expired(counter, now); | ||
++counter->nr_rendered; | ||
mutex_unlock(counter->mutex); | ||
} | ||
|
||
void | ||
fps_counter_add_skipped_frame(struct fps_counter *counter) { | ||
check_expired(counter); | ||
if (!SDL_AtomicGet(&counter->started)) { | ||
return; | ||
} | ||
|
||
mutex_lock(counter->mutex); | ||
uint32_t now = SDL_GetTicks(); | ||
check_interval_expired(counter, now); | ||
++counter->nr_skipped; | ||
mutex_unlock(counter->mutex); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.