灯光师固定时间步丢帧
gaffer fixed timestep dropping frames
我目前有一个非常简单的应用程序,它基本上是我在继续之前尝试正确整理的核心循环。
我正在关注灯光师修复您的时间步长文章,但我一定是做错了什么,因为渲染一个围绕所有三个轴旋转的四边形我已经丢帧了。基本上击中了 if(frame_time > 0.025)..
代码行,这让我相信我做的事情非常错误。更糟糕的是,我只渲染到 320x480 视口,只清除背景颜色。
这是核心功能。我有一些变量
double delta_time = 1.0f/60.0f;
double frame_time;
double start_time;
double current_time;
double accum;
static int ce_run_game() {
while (game_running > 0) {
current_time = SDL_GetTicks();
frame_time = (current_time - start_time) * 0.001f; //convert from 250ms to 0.025
printf("ft: %f\n",frame_time);
if (frame_time >= 0.025){
frame_time = 0.025;
printf("slowdown\n");
}
start_time = current_time;
while (SDL_PollEvent(&event)) {
ce_handle_events(&event);
}
if (event.type == SDL_QUIT) {
ce_quit();
}
}
accum += frame_time;
while (accum >= delta_time) {
accum -= delta_time;
ce_fixed_update();
ce_fixed_render();
}
double alpha = accum / delta_time;
// state = current_state * alpha + prev_state * (1.0 - alpha);
ce_var_update(alpha);
ce_var_render(alpha);
}
return -1;
}
通过这个简单的循环,我似乎在丢帧。相关代码的另一部分是移动代码。虽然这没什么了不起的,但它不应该造成如此大的减速。
通常帧时间打印语句在 0.016 - 0.017 左右徘徊
但有时它会提高很多甚至达到 0.85 或更高。添加上面的 if(frame_time > 0.025)
东西似乎保持在 0.16 0.17 左右,但我不确定为什么帧时间会飙升并且它们变得如此失控,特别是因为我只在屏幕上移动一个东西。
移动代码:
static void handle_event(SDL_Event* e) {
switch (e->type) {
case SDL_KEYUP:
left = right = up = down = 0;
break;
case SDL_KEYDOWN:
switch (e->key.keysym.sym) {
case SDLK_AC_BACK:
case SDLK_ESCAPE:
ce_quit();
break;
case SDLK_LEFT:
left = 1;
break;
case SDLK_RIGHT:
right = 1;
break;
case SDLK_UP:
up = 1;
break;
case SDLK_DOWN:
down = 1;
break;
}
}
}
static void variable_render(double alpha) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(ce_get_default_shader()->shader_program);
glBindVertexArray(vao);
vvv = vec3_new(1, 1, 1);
angle += 0.0015f * alpha;
if (angle >= 360.0) angle = 0.0f;
mat4_rotate(model_mat, model_mat, angle, vvv);
ce_get_view_matrices(&vview_mat, &pproj_mat, &mmvp_mat);
mat4_multi(&mmvp_mat, &vview_mat, model_mat);
mat4_multi(&mmvp_mat, &pproj_mat, &mmvp_mat);
glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mat4_get_data(&mmvp_mat));
glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, mat4_get_data(model_mat));
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, mat4_get_data(&vview_mat));
glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, mat4_get_data(&pproj_mat));
glDrawElements(GL_TRIANGLES, quad->vertex_count, GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0);
}
我必须在这里回答我自己的问题,但手头似乎有两个问题。
其中之一是由于 vsync 导致速度减慢,关闭 vsync 完全消除了速度减慢,这实际上不是我的逻辑更新速度减慢,而是渲染速度减慢。因为我使用的是固定步骤,所以这应该不是什么大问题。
另一个问题是 SDL_GetTicks() 不是很准确。我用 clock_gettime(realtimeclock...) 重写了计时函数,它的数字更加一致。
例如,SDL_GetTicks 每次更新 return 的时间从 240 毫秒到 267 毫秒不等,而 clock_gettime 在 250 毫秒的更新上保持一致。
我使用的是 SDL_SetDelay(0.25 * 1000) 并且 clock_gettime 是正确的,SDL 在那里缺少一点。
它不像 SDL 那样跨平台,但是有一个针对 windows 和 mac 的高性能计数器,可以轻松实现。
顺便说一句,这是我用于计时的代码。
#define _POSIX_C_SOURCE 199309L
#include "headers/timer_utils.h"
void tu_set_current_time(timer* time) { clock_gettime(CLOCK_REALTIME, time); }
void tu_copy_current_time(timer* to, timer* from) {
to->tv_sec = from->tv_sec;
to->tv_nsec = from->tv_nsec;
}
double tu_get_time_diff(timer* start, timer* finish) {
return (finish->tv_sec - start->tv_sec) +
(finish->tv_nsec - start->tv_nsec) * 0.000000001;// / 1E9;
}
我目前有一个非常简单的应用程序,它基本上是我在继续之前尝试正确整理的核心循环。
我正在关注灯光师修复您的时间步长文章,但我一定是做错了什么,因为渲染一个围绕所有三个轴旋转的四边形我已经丢帧了。基本上击中了 if(frame_time > 0.025)..
代码行,这让我相信我做的事情非常错误。更糟糕的是,我只渲染到 320x480 视口,只清除背景颜色。
这是核心功能。我有一些变量
double delta_time = 1.0f/60.0f;
double frame_time;
double start_time;
double current_time;
double accum;
static int ce_run_game() {
while (game_running > 0) {
current_time = SDL_GetTicks();
frame_time = (current_time - start_time) * 0.001f; //convert from 250ms to 0.025
printf("ft: %f\n",frame_time);
if (frame_time >= 0.025){
frame_time = 0.025;
printf("slowdown\n");
}
start_time = current_time;
while (SDL_PollEvent(&event)) {
ce_handle_events(&event);
}
if (event.type == SDL_QUIT) {
ce_quit();
}
}
accum += frame_time;
while (accum >= delta_time) {
accum -= delta_time;
ce_fixed_update();
ce_fixed_render();
}
double alpha = accum / delta_time;
// state = current_state * alpha + prev_state * (1.0 - alpha);
ce_var_update(alpha);
ce_var_render(alpha);
}
return -1;
}
通过这个简单的循环,我似乎在丢帧。相关代码的另一部分是移动代码。虽然这没什么了不起的,但它不应该造成如此大的减速。
通常帧时间打印语句在 0.016 - 0.017 左右徘徊
但有时它会提高很多甚至达到 0.85 或更高。添加上面的 if(frame_time > 0.025)
东西似乎保持在 0.16 0.17 左右,但我不确定为什么帧时间会飙升并且它们变得如此失控,特别是因为我只在屏幕上移动一个东西。
移动代码:
static void handle_event(SDL_Event* e) {
switch (e->type) {
case SDL_KEYUP:
left = right = up = down = 0;
break;
case SDL_KEYDOWN:
switch (e->key.keysym.sym) {
case SDLK_AC_BACK:
case SDLK_ESCAPE:
ce_quit();
break;
case SDLK_LEFT:
left = 1;
break;
case SDLK_RIGHT:
right = 1;
break;
case SDLK_UP:
up = 1;
break;
case SDLK_DOWN:
down = 1;
break;
}
}
}
static void variable_render(double alpha) {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(ce_get_default_shader()->shader_program);
glBindVertexArray(vao);
vvv = vec3_new(1, 1, 1);
angle += 0.0015f * alpha;
if (angle >= 360.0) angle = 0.0f;
mat4_rotate(model_mat, model_mat, angle, vvv);
ce_get_view_matrices(&vview_mat, &pproj_mat, &mmvp_mat);
mat4_multi(&mmvp_mat, &vview_mat, model_mat);
mat4_multi(&mmvp_mat, &pproj_mat, &mmvp_mat);
glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mat4_get_data(&mmvp_mat));
glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, mat4_get_data(model_mat));
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, mat4_get_data(&vview_mat));
glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, mat4_get_data(&pproj_mat));
glDrawElements(GL_TRIANGLES, quad->vertex_count, GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0);
}
我必须在这里回答我自己的问题,但手头似乎有两个问题。
其中之一是由于 vsync 导致速度减慢,关闭 vsync 完全消除了速度减慢,这实际上不是我的逻辑更新速度减慢,而是渲染速度减慢。因为我使用的是固定步骤,所以这应该不是什么大问题。
另一个问题是 SDL_GetTicks() 不是很准确。我用 clock_gettime(realtimeclock...) 重写了计时函数,它的数字更加一致。
例如,SDL_GetTicks 每次更新 return 的时间从 240 毫秒到 267 毫秒不等,而 clock_gettime 在 250 毫秒的更新上保持一致。
我使用的是 SDL_SetDelay(0.25 * 1000) 并且 clock_gettime 是正确的,SDL 在那里缺少一点。
它不像 SDL 那样跨平台,但是有一个针对 windows 和 mac 的高性能计数器,可以轻松实现。
顺便说一句,这是我用于计时的代码。
#define _POSIX_C_SOURCE 199309L
#include "headers/timer_utils.h"
void tu_set_current_time(timer* time) { clock_gettime(CLOCK_REALTIME, time); }
void tu_copy_current_time(timer* to, timer* from) {
to->tv_sec = from->tv_sec;
to->tv_nsec = from->tv_nsec;
}
double tu_get_time_diff(timer* start, timer* finish) {
return (finish->tv_sec - start->tv_sec) +
(finish->tv_nsec - start->tv_nsec) * 0.000000001;// / 1E9;
}