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.
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:
SDL_RenderDrawLine()
SDL_RenderSetClipRect()
rects to SDL_RenderDrawLine()
:
SDL_RENDER_LINE_METHOD
hint to 2
or 3
so that the renderer code avoids RenderDrawLineBresenham()