#include #include #include #include #include #include // only include this one in the source file with main()! #define INITIAL_FPS 60 SDL_Window* window = NULL; SDL_GLContext glcontext = NULL; GLuint program; GLuint vbo; static double FPS = INITIAL_FPS; static double FrameTime = 1.0 / INITIAL_FPS; static double PerformancePeriod; static void panicError ( void ) __attribute__((noreturn)); static double GetTime( void ) { return PerformancePeriod * SDL_GetPerformanceCounter(); } static void SetTitle ( SDL_Window *window ) { char title[48] = {0}; SDL_snprintf(title, sizeof(title), "Hello Triangle! Framelimit: %6.3f Hz", FPS); SDL_SetWindowTitle(window, title); } void panicError ( void ) { SDL_GL_DestroyContext(glcontext); SDL_DestroyWindow(window); SDL_Quit(); exit(EXIT_FAILURE); } char *readFile ( const char *fileName ) { SDL_IOStream *ioStream = SDL_IOFromFile(fileName, "rb"); if (!ioStream) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "ioStream: %s.", SDL_GetError()); panicError(); } if (SDL_SeekIO(ioStream, 0, SDL_IO_SEEK_END) == -1) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_SeekIO_END: %s.", SDL_GetError()); panicError(); } long len = SDL_TellIO(ioStream); if (len == -1) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_TellIO: %s.", SDL_GetError()); panicError(); } if (SDL_SeekIO(ioStream, 0, SDL_IO_SEEK_SET) == -1) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_IO_SEEK_SET: %s.", SDL_GetError()); panicError(); } char *res = SDL_malloc(len + 1); if (!res) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_malloc: %s.", SDL_GetError()); panicError(); } if (SDL_ReadIO(ioStream, res, len) != (size_t)len) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_ReadIO: %s.", SDL_GetError()); panicError(); } if (!SDL_CloseIO(ioStream)) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_CloseIO: %s.", SDL_GetError()); panicError(); } res[len] = 0; return res; } GLuint createShader ( const char *shaderFile, GLenum shaderType ) { const char *strShaderType; if (shaderType == GL_VERTEX_SHADER) { strShaderType = "vertex"; } else if (shaderType == GL_GEOMETRY_SHADER) { strShaderType = "geometry"; } else if (shaderType == GL_FRAGMENT_SHADER) { strShaderType = "fragment"; } else { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Unrecognized shader type."); panicError(); } GLuint shader = glCreateShader(shaderType); if (!shader) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Error create shader type %s.", strShaderType); panicError(); } GLchar *content = readFile(shaderFile); glShaderSource(shader, 1, (const GLchar **)&content, NULL); SDL_free(content); glCompileShader(shader); GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLint infoLen; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); GLchar *info = SDL_malloc(sizeof(GLchar) * (infoLen + 1)); glGetShaderInfoLog(shader, infoLen, NULL, info); SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Filed to compile %s shader:\n%s.", strShaderType, info); panicError(); } return shader; } void createProgram ( GLuint *shaders, int len ) { program = glCreateProgram(); if (!program) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Can't to create shader program."); panicError(); } for (int i = 0; i < len; i++) { glAttachShader(program, shaders[i]); } glLinkProgram(program); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLint infoLen; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); GLchar *info = SDL_malloc(sizeof(GLchar) * (infoLen + 1)); glGetProgramInfoLog(program, infoLen, NULL, info); SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "Link failed: %s.", info); panicError(); } } void createBuffer ( void ) { float vertices[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f }; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); } void startShaderProg ( void ) { createBuffer(); GLuint shaders[] = { createShader("vertex.glsl", GL_VERTEX_SHADER), createShader("fragment.glsl", GL_FRAGMENT_SHADER) }; int len = sizeof(shaders) / sizeof(shaders[0]); createProgram(shaders, len); for (int i = 0; i < len; i++) { glDeleteShader(shaders[i]); } } void _GLBuffer( void ) { glClearColor(0.0f, 0.0f, 1.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(program); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vbo); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableVertexAttribArray(0); glUseProgram(0); } int main( int argc, char* argv[] ) { const int WIDTH = 640; const int HEIGHT = 480; int delay_threshold = 2; int r, g, b, GLJ, GLN = 0; double sleep_time = 0; double time_counter = 0; bool loopShouldStop = false; SDL_SetHint(SDL_HINT_RENDER_VSYNC, 0); if (!SDL_Init(SDL_INIT_VIDEO)) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_Init failed: %s.", SDL_GetError()); } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); PerformancePeriod = 1.0 / SDL_GetPerformanceFrequency(); time_counter = GetTime(); window = SDL_CreateWindow("Hello SDL", WIDTH, HEIGHT, SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (!window) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_CreateWindow: %s.", SDL_GetError()); panicError(); } glcontext = SDL_GL_CreateContext(window); if (!glcontext) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_GL_CreateContext: %s.", SDL_GetError()); panicError(); } SetTitle(window); SDL_HideCursor(); glewExperimental = GL_TRUE; GLenum err = glewInit(); if (err != GLEW_OK) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "glewInit: %s.", glewGetErrorString(err)); panicError(); } SDL_LogVerbose(SDL_LOG_CATEGORY_RENDER, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); err = SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &GLJ); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &GLN); SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r); SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g); SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b); SDL_Log("%s", SDL_GetError()); SDL_LogVerbose(SDL_LOG_CATEGORY_RENDER, "Red size: %d, Green size: %d, Blue size: %d, OpenGL: %d.%d.", r, g, b, GLJ, GLN); SDL_GL_SetSwapInterval(0); startShaderProg(); while (!loopShouldStop) { SDL_Event e; SDL_zero(e); while (SDL_PollEvent(&e)) { if (e.type == SDL_EVENT_QUIT) loopShouldStop = true; else if (e.type == SDL_EVENT_KEY_DOWN) { if (e.key.key == SDLK_X) { FPS -= 0.1; if (FPS < 1) { FPS = 1; } FrameTime = 1.0 / FPS; SetTitle(window); } else if (e.key.key == SDLK_C) { FPS += 0.1; FrameTime = 1.0 / FPS; SetTitle(window); } if (e.key.key == SDLK_ESCAPE) loopShouldStop = true; } } _GLBuffer(); if (!SDL_GL_SwapWindow(window)) { SDL_LogCritical(SDL_LOG_CATEGORY_ASSERT, "SDL_GL_SwapWwindow: %s.", SDL_GetError()); panicError(); } sleep_time = time_counter - GetTime(); if (sleep_time * 1000 > delay_threshold) { Uint32 ms = (Uint32)(sleep_time * 1000) - delay_threshold; SDL_Delay(ms); if (time_counter < GetTime()) { delay_threshold++; SDL_Log("Slept too long. Increased threshold to %u ms.", delay_threshold); } } while (time_counter > GetTime()) { /* Waiting for the right moment. */ } time_counter += FrameTime; } SDL_DestroyWindow(window); SDL_GL_DestroyContext(glcontext); SDL_Quit(); exit(EXIT_SUCCESS); }