C程序中使用SDL2的内存泄漏。

3
我的程序在屏幕上绘制一个可以移动的3D立方体。当程序运行时或者当我尝试移动立方体时,会出现一些内存泄漏问题。
此外,当立方体靠近屏幕并“穿过”它时,我会遇到一些内存使用量剧增的情况。有时候,内存使用量甚至超过了几个GB,导致我的电脑卡死。
我看到有人说sdl2库本身存在一些轻微的内存泄漏问题,但这已经有点太严重了 :o。

screenshot of cube rendered with perspective

这是代码:
#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;
}

我尝试使用Dr. Memory,它产生了以下结果:
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

整个日志文件相当长,所以我只包括前两个错误。

你在调试器中运行了你的代码,看看错误发生在哪里,然后再次运行它,在失败附近设置一个断点,这样你就可以小心地向前迈进,并观察在那一点之前发生了什么? - tadman
我调试了函数drawCube,它可以访问点数组之外的位置,但是每个变量似乎都被正确初始化,并且正确地索引了数组。 - CopperLover
1个回答

1
在“不好”的相机位置,确保传入SDL_RenderDrawLine()的线条不要太长(比如数百万或数十亿像素)。
如果它们太长,并且SDL处于SDL_RENDERLINEMETHOD_POINTS模式下,它将会调用SDL_render.c:RenderDrawLineBresenham(),该函数会根据线条长度进行内部分配。
处理超长线条的几个选项:

1
是的,这些行太长了,非常感谢! - CopperLover

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接