'전체 글'에 해당되는 글 542건

  1. 2025.04.22 [SDL] Framerate Per Second FPS

[SDL] Framerate Per Second FPS

C, C++ 2025. 4. 22. 22:28 |
반응형

SDL 사용시 FPS를 고정해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdio.h>
 
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
 
int screen_width = 640;
int screen_height = 480;
 
Uint64 start;
Uint64 end;
float fps;
 
typedef struct {
    float x, y;
    float vx, vy;
} s_particle;
 
const int number_particles = 400;
s_particle particles[number_particles];
 
// 파티클 이미지 텍스쳐와 렉트
SDL_Texture* texture;
SDL_FRect rect;
 
void spawn_particle(s_particle* pt) {
    pt->= (SDL_randf() * 2 - 1* screen_width * 10;
    pt->= SDL_randf() * screen_height * -2;
    // 넓은 범위에서 눈이 생성되게 해서 실행 초기에 눈이 쏟아져 내리는걸 방지.
    pt->vx = SDL_randf() * 0.5f; // 눈은 오른쪽으로만 흩날린다.
    pt->vy = SDL_randf() + 0.8f;
}
 
void draw_particle(s_particle pt[]) {
    for (int i = 0; i < number_particles; i++) {
        s_particle* particle = &particles[i];
        particle->+= particle->vx;
        particle->+= particle->vy;
        // 눈이 화면 왼쪽에서도 바람에 날려 올 수 있도록 생존 범위 조정.
        if (particle->> screen_width || particle->< -screen_width || particle->> screen_height)
            spawn_particle(particle);
 
        // 텍스쳐 그리기
        rect.x = particle->- rect.w / 2;
        rect.y = particle->- rect.h / 2;
        SDL_RenderTexture(renderer, texture, NULL&rect);
 
        //SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
        //SDL_RenderPoint(renderer, particle->x, particle->y);
    }
}
 
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
    SDL_SetAppMetadata("Example""1.0""sean");
 
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
    // A variable controlling whether updates to the SDL screen surface should be synchronized
    // with the vertical refresh, to avoid tearing.
 
    if (!SDL_CreateWindowAndRenderer("example", screen_width, screen_height, 0&window, &renderer)) {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    for (int i = 0; i < number_particles; i++) {
        spawn_particle(&particles[i]);
    }
 
    // 비트맵 로드
    SDL_Surface* bmpSurface = SDL_LoadBMP("snow.bmp");
    if (!bmpSurface) {
        SDL_Log("Couldn't load BMP: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    // 컬러키(투명) 설정
    SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapSurfaceRGB(bmpSurface, 0x000x000x00));
    //SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapRGB(SDL_GetPixelFormatDetails(bmpSurface->format), NULL, 0x00, 0x00, 0x00));
    rect = { 00, (float)bmpSurface->w, (float)bmpSurface->h };
    texture = SDL_CreateTextureFromSurface(renderer, bmpSurface);
 
    SDL_DestroySurface(bmpSurface);
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
    switch (event->type) {
    case SDL_EVENT_QUIT:
        return SDL_APP_SUCCESS;
    case SDL_EVENT_KEY_DOWN:
        printf("Key pressed: %s\n", SDL_GetKeyName(event->key.key));
        if (event->key.key == SDLK_ESCAPE)
            return SDL_APP_SUCCESS;
        break;
    default:
        break;
    }
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppIterate(void* appstate)
{
    start = SDL_GetPerformanceCounter();
 
    {
        SDL_SetRenderDrawColor(renderer, 000, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        draw_particle(particles);
 
        SDL_RenderPresent(renderer);
    }
 
    end = SDL_GetPerformanceCounter();
    fps = 1.0f / ((end - start) / (float)SDL_GetPerformanceFrequency());
    printf("FPS: %f\n", fps);
 
    return SDL_APP_CONTINUE;
}
 
void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
    SDL_DestroyTexture(texture);
 
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}
 

 

 

위 내용은 눈 내리는 코드와 실행 결과이다. FPS를 고정하기 위해 SDL_AppInit()에서 수직 동기화(Vertical Synchronization)를 설정했다. 특정 FPS를 간단히 지정하고 싶다면 아래와 같이 해도 된다.

SDL_Delay(1000 / 원하는 FPS)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SDL_AppResult SDL_AppIterate(void* appstate)
{
    start = SDL_GetPerformanceCounter();
    SDL_Delay(1000 / 60);
 
    {
        SDL_SetRenderDrawColor(renderer, 000, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        draw_particle(particles);
 
        SDL_RenderPresent(renderer);
    }
 
    end = SDL_GetPerformanceCounter();
    fps = 1.0f / ((end - start) / (float)SDL_GetPerformanceFrequency());
    printf("FPS: %f\n", fps);
 
    return SDL_APP_CONTINUE;
}
 

 

 

아니면 SDL3_gfx 라이브러리를 사용할 수도 있다. 아래 링크에서 다운로드하자.

SDL3_gfx

SDL3_gfx-1.0.1.zip
4.99MB

 

 

여기서는 FPS관련 파일만 프로젝트에 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdio.h>
#include "SDL3_framerate.h"
 
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
 
int screen_width = 640;
int screen_height = 480;
 
Uint64 start;
Uint64 end;
float fps;
 
// FPS 관리자
FPSmanager fpsm;
 
typedef struct {
    float x, y;
    float vx, vy;
} s_particle;
 
const int number_particles = 400;
s_particle particles[number_particles];
 
// 파티클 이미지 텍스쳐와 렉트
SDL_Texture* texture;
SDL_FRect rect;
 
void spawn_particle(s_particle* pt) {
    pt->= (SDL_randf() * 2 - 1* screen_width * 10;
    pt->= SDL_randf() * screen_height * -2;
    // 넓은 범위에서 눈이 생성되게 해서 실행 초기에 눈이 쏟아져 내리는걸 방지.
    pt->vx = SDL_randf() * 0.5f; // 눈은 오른쪽으로만 흩날린다.
    pt->vy = SDL_randf() + 0.8f;
}
 
void draw_particle(s_particle pt[]) {
    for (int i = 0; i < number_particles; i++) {
        s_particle* particle = &particles[i];
        particle->+= particle->vx;
        particle->+= particle->vy;
        // 눈이 화면 왼쪽에서도 바람에 날려 올 수 있도록 생존 범위 조정.
        if (particle->> screen_width || particle->< -screen_width || particle->> screen_height)
            spawn_particle(particle);
 
        // 텍스쳐 그리기
        rect.x = particle->- rect.w / 2;
        rect.y = particle->- rect.h / 2;
        SDL_RenderTexture(renderer, texture, NULL&rect);
 
        //SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
        //SDL_RenderPoint(renderer, particle->x, particle->y);
    }
}
 
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
    SDL_SetAppMetadata("Example""1.0""sean");
 
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    // FPS 관리자 초기화. FPS=60
    SDL_initFramerate(&fpsm);
    SDL_setFramerate(&fpsm, 60);
    printf("Set FPS: %d\n", SDL_getFramerate(&fpsm));
 
    if (!SDL_CreateWindowAndRenderer("example", screen_width, screen_height, 0&window, &renderer)) {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    for (int i = 0; i < number_particles; i++) {
        spawn_particle(&particles[i]);
    }
 
    // 비트맵 로드
    SDL_Surface* bmpSurface = SDL_LoadBMP("snow.bmp");
    if (!bmpSurface) {
        SDL_Log("Couldn't load BMP: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    // 컬러키(투명) 설정
    SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapSurfaceRGB(bmpSurface, 0x000x000x00));
    //SDL_SetSurfaceColorKey(bmpSurface, true, SDL_MapRGB(SDL_GetPixelFormatDetails(bmpSurface->format), NULL, 0x00, 0x00, 0x00));
    rect = { 00, (float)bmpSurface->w, (float)bmpSurface->h };
    texture = SDL_CreateTextureFromSurface(renderer, bmpSurface);
 
    SDL_DestroySurface(bmpSurface);
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
    switch (event->type) {
    case SDL_EVENT_QUIT:
        return SDL_APP_SUCCESS;
    case SDL_EVENT_KEY_DOWN:
        printf("Key pressed: %s\n", SDL_GetKeyName(event->key.key));
        if (event->key.key == SDLK_ESCAPE)
            return SDL_APP_SUCCESS;
        break;
    default:
        break;
    }
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppIterate(void* appstate)
{
    start = SDL_GetPerformanceCounter();
 
    {
        SDL_SetRenderDrawColor(renderer, 000, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        draw_particle(particles);
 
        SDL_RenderPresent(renderer);
    }
 
    // FPS를 맞추기 위한 지연.
    SDL_framerateDelay(&fpsm);
 
    end = SDL_GetPerformanceCounter();
    fps = 1.0f / ((end - start) / (float)SDL_GetPerformanceFrequency());
    printf("Actual FPS: %f\n", fps);
 
    return SDL_APP_CONTINUE;
}
 
void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
    SDL_DestroyTexture(texture);
 
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}
 

 

 

프로그램을 실행하면 FPS가 60에 가깝게 유지된다.

 

SDL_AppInit()에서 FPS 관리자 초기화 부분을 제일 아래로 옮기면 최초 FPS가 좀 더 낮은 수치가 되지만 별 의미는 없는거 같다.

 

반응형
Posted by J-sean
: