英文:
Memory leak in C program using SDL2
问题
我的程序在屏幕上绘制了一个可以移动的3D立方体。当程序运行时或者当我尝试移动立方体时,会出现一些内存泄漏的问题。
此外,当立方体靠近屏幕并“穿过”屏幕时,我会遇到内存使用量的巨大峰值。有时内存使用量会达到几个GB,导致我的计算机冻结。
我看到有人说sdl2库本身存在一些轻微的泄漏问题,但这有点过分了。
以下是代码:
#define SDL_MAIN_HANDLED
#include <stdio.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
const int gWidth = 640;
const int gHeight = 480;
const float moveSpeed = 5.0f;
const int fl = 300;
const int centerX = gWidth / 2;
const int centerY = gHeight / 2;
const int centerZ = 1000;
SDL_Window* gWindow = NULL;
SDL_Renderer* gRenderer = NULL;
struct Vector3 {
float x; float y; float z;
};
bool init() {
if(SDL_Init(SDL_INIT_VIDEO) != 0) {
printf("SDL Error: %s", SDL_GetError());
return false;
}
gWindow = SDL_CreateWindow("hi", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, gWidth, gHeight, 0);
if(gWindow == NULL) {
printf("SDL Error: %s", SDL_GetError());
return false;
}
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_PRESENTVSYNC);
if(gWindow == NULL) {
printf("SDL Error: %s", SDL_GetError());
return false;
}
return true;
}
void close() {
SDL_DestroyRenderer(gRenderer);
SDL_DestroyWindow(gWindow);
SDL_Quit();
}
// 移动8个点
void translateModel(struct Vector3* input, float x, float y, float z) {
for(int i = 0; i < 8; i++) {
input[i].x += x;
input[i].y += y;
input[i].z += z;
}
}
// 将3D点投影到2D屏幕上
SDL_Point projectPoint(struct Vector3 input) {
float perspective = 1.0f;
float distance = fl + input.z + centerZ;
// 比较浮点数
if(distance > 1 || distance < -1) {
perspective = fl / distance;
}
SDL_Point p;
// + centerX 使原点位于屏幕中间
p.x = input.x * perspective + centerX;
p.y = input.y * perspective + centerY;
return p;
}
// 连接8个点形成立方体
void drawCube(struct Vector3* input) {
for(int i = 0; i < 4; i++) {
SDL_Point p0 = projectPoint(input[i]); // 0 1 1 2 2 3 3 0
SDL_Point p1 = projectPoint(input[(i + 1) % 4]);
SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
}
for(int i = 0; i < 4; i++) {
SDL_Point p0 = projectPoint(input[i + 4]);
SDL_Point p1 = projectPoint(input[(i + 1) % 4 + 4]);
SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
}
for(int i = 0; i < 4; i++) {
SDL_Point p0 = projectPoint(input[i]);
SDL_Point p1 = projectPoint(input[(i + 4)]);
SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
}
}
void draw(struct Vector3* input) {
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(gRenderer);
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
drawCube(input);
SDL_RenderPresent(gRenderer);
}
int main() {
// 初始化SDL相关内容
init();
bool isRunning = true;
SDL_Event e;
const Uint8* gKeyStates = SDL_GetKeyboardState(NULL);
struct Vector3 points[8] = {
[0] = {-150, -150, 150},
[1] = {150, -150, 150},
[2] = {150, 150, 150},
[3] = {-150, 150, 150},
[4] = {-150, -150, -150},
[5] = {150, -150, -150},
[6] = {150, 150, -150},
[7] = {-150, 150, -150},
};
while(isRunning) {
SDL_PollEvent(&e);
if(e.type == SDL_QUIT) {
return false;
}
if(gKeyStates[SDL_SCANCODE_LEFT]) { // 添加“tickrate”以使速度与不同帧率匹配
translateModel(points, -moveSpeed, 0, 0);
}
if(gKeyStates[SDL_SCANCODE_RIGHT]) {
translateModel(points, moveSpeed, 0, 0);
}
if(gKeyStates[SDL_SCANCODE_UP]) {
translateModel(points, 0, 0, -moveSpeed);
}
if(gKeyStates[SDL_SCANCODE_DOWN]) {
translateModel(points, 0, 0, moveSpeed);
}
draw(points);
}
close();
return 0;
}
我尝试使用Dr. Memory进行检查,结果如下:
错误#1:超出堆栈顶部的非寻址访问:读取0x0000002b24fff860-0x0000002b24fff868 8个字节
# 0 .text
# 1 _pei386_runtime_relocator
# 2 __tmainCRTStartup
# 3 .l_start
# 4 KERNEL32.dll!BaseThreadInitThunk
注意:@0:00:00.253 在线程6472中
注意:0x0000002b24fff860指的是堆栈顶部0x0000002b24fffb68之上776个字节
注意:指令:or $0x0000000000000000 (%rcx) -> (%rcx)
错误#2:未初始化的读取:读取0x0000002b24ffeb7c-0x0000002b24ffeb80 4个字节,范围为0x0000002b24ffeb78-0x0000002b24ffeb80
# 0 系统调用NtGdiOpenDCW参数值#5
# 1 gdi32full.dll!hdcCreateDCW +0xb (0x00007ff846b2f8e2 <gdi32full.dll+0x1f8e2>)
# 2 GDI32.dll!bCreateDCW
# 3 GDI32.dll!CreateDCW
# 4 SDL2.dll!? +0x0 (0x00007ff8042e484e <SDL2.dll+0x12484e>)
# 5 SDL2.dll!? +0x0 (0x00007ff8042e4bd3 <SDL2.dll+0x124bd3>)
# 6 USER32.dll!_ClientMonitorEnumProc
# 7 SDL2.dll!? +0x0 (0x00007ff8042e4e57 <SDL2.dll+0x124e57>)
# 8 SDL2.dll!? +0x0 (0x00007ff8042e8546 <SDL2.dll+0x128546>)
# 9 SDL2.dll!? +0x0 (0x00007ff8042aeea0 <SDL2.dll+0xeeea0>)
#10 SDL2.dll!? +0x0 (0x00007ff8041c16e7 <SDL2.dll+0x16e7>)
#11 init
#12 main
注意:@0:00:01.236 在线程6472中
整个日志文件非常长,所以我只包含了前两个错误。
英文:
My program draws a 3D cube on a screen which you can move. There are some memory leaking over time when the program is just running or when I try to move the cube.
Also, I experience some huge spikes in memory usage whenever the cube comes close to the screen and "goes past it" It reaches insane values sometimes over GBs of ram to the point when my computer freezes.
I've seen people saying that there are some minor leaks in the sdl2 library itself and they should be ignored but this is a bit too much :o.
Here's the code:
#define SDL_MAIN_HANDLED
#include <stdio.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
const int gWidth = 640;
const int gHeight = 480;
const float moveSpeed = 5.0f;
const int fl = 300;
const int centerX = gWidth / 2;
const int centerY = gHeight / 2;
const int centerZ = 1000;
SDL_Window* gWindow = NULL;
SDL_Renderer* gRenderer = NULL;
struct Vector3 {
float x; float y; float z;
};
bool init() {
if(SDL_Init(SDL_INIT_VIDEO) != 0) {
printf("SDL Erorr: %s", SDL_GetError());
return false;
}
gWindow = SDL_CreateWindow("hi", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, gWidth, gHeight, 0);
if(gWindow == NULL) {
printf("SDL Erorr: %s", SDL_GetError());
return false;
}
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_PRESENTVSYNC);
if(gWindow == NULL) {
printf("SDL Erorr: %s", SDL_GetError());
return false;
}
return true;
}
void close() {
SDL_DestroyRenderer(gRenderer);
SDL_DestroyWindow(gWindow);
SDL_Quit();
}
// moves 8 points
void translateModel(struct Vector3* input, float x, float y, float z) {
for(int i = 0; i < 8; i++) {
input[i].x += x;
input[i].y += y;
input[i].z += z;
}
}
// project 3D point onto the 2D screen
SDL_Point projectPoint(struct Vector3 input) {
float perspective = 1.0f;
float distance = fl + input.z + centerZ;
// comparing floating points
if(distance > 1 || distance < -1) {
perspective = fl / distance;
}
SDL_Point p;
// + centerX makes origin sit in the middle of the screen
p.x = input.x * perspective + centerX;
p.y = input.y * perspective + centerY;
return p;
}
// connects 8 points to form a cube
void drawCube(struct Vector3* input) {
for(int i = 0; i < 4; i++) {
SDL_Point p0 = projectPoint(input[i]); // 0 1 1 2 2 3 3 0
SDL_Point p1 = projectPoint(input[(i + 1) % 4]);
SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
}
for(int i = 0; i < 4; i++) {
SDL_Point p0 = projectPoint(input[i + 4]);
SDL_Point p1 = projectPoint(input[(i + 1) % 4 + 4]);
SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
}
for(int i = 0; i < 4; i++) {
SDL_Point p0 = projectPoint(input[i]);
SDL_Point p1 = projectPoint(input[(i + 4)]);
SDL_RenderDrawLine(gRenderer, p0.x, p0.y, p1.x, p1.y);
}
}
void draw(struct Vector3* input) {
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(gRenderer);
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
drawCube(input);
SDL_RenderPresent(gRenderer);
}
int main() {
// initialize SDL stuff
init();
bool isRunning = true;
SDL_Event e;
const Uint8* gKeyStates = SDL_GetKeyboardState(NULL);
struct Vector3 points[8] = {
[0] = {-150, -150, 150},
[1] = {150, -150, 150},
[2] = {150, 150, 150},
[3] = {-150, 150, 150},
[4] = {-150, -150, -150},
[5] = {150, -150, -150},
[6] = {150, 150, -150},
[7] = {-150, 150, -150},
};
while(isRunning) {
SDL_PollEvent(&e);
if(e.type == SDL_QUIT) {
return false;
}
if(gKeyStates[SDL_SCANCODE_LEFT]) { // add "tickrate" so that speed matches with diffrent frame rates
translateModel(points, -moveSpeed, 0, 0);
}
if(gKeyStates[SDL_SCANCODE_RIGHT]) {
translateModel(points, moveSpeed, 0, 0);
}
if(gKeyStates[SDL_SCANCODE_UP]) {
translateModel(points, 0, 0, -moveSpeed);
}
if(gKeyStates[SDL_SCANCODE_DOWN]) {
translateModel(points, 0, 0, moveSpeed);
}
draw(points);
}
close();
return 0;
}
I tried to use Dr. Memory and it produces this:
Error #1: UNADDRESSABLE ACCESS beyond top of stack: reading 0x0000002b24fff860-0x0000002b24fff868 8 byte(s)
# 0 .text
# 1 _pei386_runtime_relocator
# 2 __tmainCRTStartup
# 3 .l_start
# 4 KERNEL32.dll!BaseThreadInitThunk
Note: @0:00:00.253 in thread 6472
Note: 0x0000002b24fff860 refers to 776 byte(s) beyond the top of the stack 0x0000002b24fffb68
Note: instruction: or $0x0000000000000000 (%rcx) -> (%rcx)
Error #2: UNINITIALIZED READ: reading 0x0000002b24ffeb7c-0x0000002b24ffeb80 4 byte(s) within 0x0000002b24ffeb78-0x0000002b24ffeb80
# 0 system call NtGdiOpenDCW parameter value #5
# 1 gdi32full.dll!hdcCreateDCW +0xb1 (0x00007ff846b2f8e2 <gdi32full.dll+0x1f8e2>)
# 2 GDI32.dll!bCreateDCW
# 3 GDI32.dll!CreateDCW
# 4 SDL2.dll!? +0x0 (0x00007ff8042e484e <SDL2.dll+0x12484e>)
# 5 SDL2.dll!? +0x0 (0x00007ff8042e4bd3 <SDL2.dll+0x124bd3>)
# 6 USER32.dll!_ClientMonitorEnumProc
# 7 SDL2.dll!? +0x0 (0x00007ff8042e4e57 <SDL2.dll+0x124e57>)
# 8 SDL2.dll!? +0x0 (0x00007ff8042e8546 <SDL2.dll+0x128546>)
# 9 SDL2.dll!? +0x0 (0x00007ff8042aeea0 <SDL2.dll+0xeeea0>)
#10 SDL2.dll!? +0x0 (0x00007ff8041c16e7 <SDL2.dll+0x16e7>)
#11 init
#12 main
Note: @0:00:01.236 in thread 6472
The whole log file is pretty long so I just included first 2 errors.
答案1
得分: 1
在进行“不好”的相机位置时,请确保传递给SDL_RenderDrawLine()
的线条不是超长的(比如数百万或数十亿像素)。
如果它们是超长的,并且SDL处于SDL_RENDERLINEMETHOD_POINTS
模式,它将会调用SDL_render.c:RenderDrawLineBresenham()
,该函数会根据线条的像素长度进行内部分配。
处理超长线条的几个选项:
- 在调用
SDL_RenderDrawLine()
之前将线条裁剪到渲染器范围内。 - 等待SDL将
SDL_RenderSetClipRect()
矩形应用于SDL_RenderDrawLine()
: - 将
SDL_RENDER_LINE_METHOD
提示设置为2
或3
,以使渲染器代码避免使用RenderDrawLineBresenham()
。
英文:
During the 'bad' camera positions make sure the lines you're passing in to SDL_RenderDrawLine()
aren't super-long (like millions or billions of pixels).
If they are super-long and SDL is in SDL_RENDERLINEMETHOD_POINTS
mode it's going to hit SDL_render.c:RenderDrawLineBresenham()
which does internal allocs proportional to the length of the line in pixels.
A few options for handling super-long lines:
- Clip your lines to the renderer extents before calling
SDL_RenderDrawLine()
- Wait for SDL to apply
SDL_RenderSetClipRect()
rects toSDL_RenderDrawLine()
: - Set the
SDL_RENDER_LINE_METHOD
hint to2
or3
so that the renderer code avoidsRenderDrawLineBresenham()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论