#include "Game.hpp" FramerateIndicator::FramerateIndicator(Node* parent) : Sprite(parent) { get_delegate().subscribe(&FramerateIndicator::respond, this); hide(); } void FramerateIndicator::respond(SDL_Event& event) { if (get_delegate().compare(event, "toggle-framerate")) { toggle_hidden(); } } SDL_Surface* FramerateIndicator::get_surface() { std::string padded = sfw::pad(get_root()->frame_count_this_second, 2); SDL_Surface* shaded = TTF_RenderText_Shaded( get_root()->bp_mono_font, padded.c_str(), {0, 0, 0}, {255, 255, 255}); // SDL_Surface* converted = SDL_ConvertSurfaceFormat( // shaded, SDL_PIXELFORMAT_ARGB8888, 0); // SDL_Surface* flipped = zoomSurface(converted, 1, -1, SMOOTHING_OFF); // SDL_FreeSurface(shaded); // SDL_FreeSurface(converted); if (!shaded) { get_root()->print_sdl_error("Could not create text"); } return shaded; } void FramerateIndicator::refresh() { if (!is_hidden() && get_root()->bp_mono_font != NULL) { unload(); SDL_Surface* surface = get_surface(); SDL_Texture* texture = SDL_CreateTextureFromSurface(get_root()->get_renderer(), surface); add_frame(texture); SDL_FreeSurface(surface); box.set_ne(get_display().get_window_box().get_ne()); } } Game::Game() { frame_length_history.reserve(5000); set_framerate(get_configuration()["display"]["framerate"]); delegate.subscribe(&Game::handle_quit_event, this, SDL_QUIT); SDL_Log("GLEW %s", glewGetString(GLEW_VERSION)); putenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN=0"); // putenv("SDL_VIDEO_CENTERED=0"); SDL_version version; SDL_GetVersion(&version); SDL_Log("SDL %d.%d.%d", version.major, version.minor, version.patch); fprintf(stderr, "stderr test message\n"); SDL_SetMainReady(); // SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); print_gl_attributes(); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { print_sdl_error("SDL could not initialize"); flag_to_end(); } // SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); print_gl_attributes(); std::vector window_size = get_configuration()["display"]["dimensions"]; window = SDL_CreateWindow( get_configuration()["display"]["title"].get_ref().c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_size[0], window_size[1], SDL_WINDOW_OPENGL); if (window == NULL) { print_sdl_error("Could not create window"); flag_to_end(); } SDL_ShowCursor(0); if (TTF_Init() < 0) { print_sdl_error("Could not initialize SDL ttf"); flag_to_end(); } else { SDL_Log("initialized SDL ttf %d.%d.%d", SDL_TTF_MAJOR_VERSION, SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL); } if ((bp_mono_font = TTF_OpenFont("BPmono.ttf", 14)) == NULL) { print_error("Could not load BPmono.ttf"); } #if !defined(__EMSCRIPTEN__) if (Mix_Init(MIX_INIT_FLAC) == 0) { print_sdl_error("Could not initialize SDL mixer"); flag_to_end(); } else { SDL_Log("initialized SDL mixer %d.%d.%d", SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL); } #endif if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 1024) < 0) { print_sdl_error("Could not set up audio"); } SDL_Log("Using audio driver: %s", SDL_GetCurrentAudioDriver()); const int audio_device_count = SDL_GetNumAudioDevices(SDL_TRUE); for (int ii = 0; ii < audio_device_count; ii++) { SDL_Log("Found audio capture device %i: %s", ii, SDL_GetAudioDeviceName(ii, SDL_TRUE)); } #if SDL_BYTEORDER == SDL_BIG_ENDIAN SDL_Log("big endian"); #else SDL_Log("little endian"); #endif last_frame_timestamp = SDL_GetTicks(); } Game::~Game() { get_delegate().unsubscribe(this); } void Game::print_error(std::string message) { std::cerr << message << std::endl; } void Game::print_sdl_error(std::string message) { std::cerr << message << " " << SDL_GetError() << std::endl; } void Game::print_gl_attributes() { int major, minor; SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); SDL_Log("GL CONTEXT: %i, %i", major, minor); int r, g, b, a, buf; 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_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &a); SDL_GL_GetAttribute(SDL_GL_BUFFER_SIZE, &buf); SDL_Log("GL PIXELS: red: %i green: %i blue: %i alpha: %i", r, g, b, a); } void Game::print_frame_length_history() { for (float& frame_length : frame_length_history) { std::cout << frame_length << ", "; } std::cout << std::endl; } void Game::load_sdl_context() { if (glcontext != NULL) { SDL_GL_DeleteContext(glcontext); glcontext = NULL; } if ((renderer = SDL_CreateRenderer(window, -1, 0)) == NULL) { print_sdl_error("Could not create renderer"); flag_to_end(); } else { SDL_RendererInfo info; SDL_GetRendererInfo(renderer, &info); SDL_Log("renderer name: %s, flags: %i, texture formats: %i, " "max texture w: %i, max texture h: %i", info.name, info.flags, info.num_texture_formats, info.max_texture_width, info.max_texture_height); } is_gl_context = false; log_display_mode(); } void Game::load_gl_context() { if (renderer != NULL) { SDL_DestroyRenderer(renderer); renderer = NULL; } // SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); print_gl_attributes(); if ((glcontext = SDL_GL_CreateContext(window)) == NULL) { print_sdl_error("Could not get GL context"); flag_to_end(); } GLenum error = glewInit(); if (error != GLEW_OK) { std::ostringstream message; message << "GLEW could not initialize " << glewGetErrorString(error); print_error(message.str()); } SDL_Log("OpenGL %s, renderer %s, shading language %s", glGetString(GL_VERSION), glGetString(GL_RENDERER), glGetString(GL_SHADING_LANGUAGE_VERSION)); is_gl_context = true; log_display_mode(); } bool Game::log_gl_errors(std::string suffix) { GLenum error; bool error_logged = false; while ((error = glGetError()) != GL_NO_ERROR) { error_logged = true; if (error == GL_INVALID_ENUM) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "GL_INVALID_ENUM, an unacceptable value is specified for an " "enumerated argument %s", suffix.c_str()); } else if (error == GL_INVALID_VALUE) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "GL_INVALID_VALUE, a numeric argument is out of range %s", suffix.c_str()); } else if (error == GL_INVALID_OPERATION) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "GL_INVALID_OPERATION, the specified operation is not allowed " "in the current state %s", suffix.c_str()); } else if (error == GL_INVALID_FRAMEBUFFER_OPERATION) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "GL_INVALID_FRAMEBUFFER_OPERATION, the framebuffer object is " "not complete %s", suffix.c_str()); } else if (error == GL_OUT_OF_MEMORY) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "GL_OUT_OF_MEMORY, there is not enough memory left to execute " "the command %s", suffix.c_str()); } else if (error == GL_STACK_UNDERFLOW) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "GL_STACK_UNDERFLOW, an attempt has been made to perform an " "operation that would cause an internal stack to underflow. %s", suffix.c_str()); } else if (error == GL_STACK_OVERFLOW) { SDL_LogError( SDL_LOG_CATEGORY_APPLICATION, "GL_STACK_OVERFLOW, an attempt has been made to perform an " "operation that would cause an internal stack to overflow. %s", suffix.c_str()); } } return error_logged; } // from SDL_GetCurrentDisplayMode.html void Game::log_display_mode() { SDL_DisplayMode current; for (int ii = 0; ii < SDL_GetNumVideoDisplays(); ii++) { int mode = SDL_GetCurrentDisplayMode(ii, ¤t); if (mode != 0) { SDL_Log("Could not get display mode for video display #%d: %s", ii, SDL_GetError()); } else { SDL_Log("Display #%d: display mode is %dx%dpx @ %dhz %s", ii, current.w, current.h, current.refresh_rate, get_pixel_format_string(current.format).c_str()); } } } void Game::log_surface_format(SDL_Surface* surface, std::string preface) { SDL_PixelFormat* format = surface->format; std::string pixel_format = get_pixel_format_string(format->format); SDL_Log("%s bpp: %i mask: %i %i %i %i format: %s", preface.c_str(), format->BytesPerPixel, format->Rmask, format->Gmask, format->Bmask, format->Amask, pixel_format.c_str()); } std::string Game::get_pixel_format_string(Uint32 format) { std::string pixel_format; if (format == SDL_PIXELFORMAT_UNKNOWN) { pixel_format = "SDL_PIXELFORMAT_UNKNOWN"; } else if (format == SDL_PIXELFORMAT_INDEX1LSB) { pixel_format = "SDL_PIXELFORMAT_INDEX1LSB"; } else if (format == SDL_PIXELFORMAT_INDEX1MSB) { pixel_format = "SDL_PIXELFORMAT_INDEX1MSB"; } else if (format == SDL_PIXELFORMAT_INDEX4LSB) { pixel_format = "SDL_PIXELFORMAT_INDEX4LSB"; } else if (format == SDL_PIXELFORMAT_INDEX4MSB) { pixel_format = "SDL_PIXELFORMAT_INDEX4MSB"; } else if (format == SDL_PIXELFORMAT_INDEX8) { pixel_format = "SDL_PIXELFORMAT_INDEX8"; } else if (format == SDL_PIXELFORMAT_RGB332) { pixel_format = "SDL_PIXELFORMAT_RGB332"; } else if (format == SDL_PIXELFORMAT_RGB444) { pixel_format = "SDL_PIXELFORMAT_RGB444"; } else if (format == SDL_PIXELFORMAT_RGB555) { pixel_format = "SDL_PIXELFORMAT_RGB555"; } else if (format == SDL_PIXELFORMAT_BGR555) { pixel_format = "SDL_PIXELFORMAT_BGR555"; } else if (format == SDL_PIXELFORMAT_ARGB4444) { pixel_format = "SDL_PIXELFORMAT_ARGB4444"; } else if (format == SDL_PIXELFORMAT_RGBA4444) { pixel_format = "SDL_PIXELFORMAT_RGBA4444"; } else if (format == SDL_PIXELFORMAT_ABGR4444) { pixel_format = "SDL_PIXELFORMAT_ABGR4444"; } else if (format == SDL_PIXELFORMAT_BGRA4444) { pixel_format = "SDL_PIXELFORMAT_BGRA4444"; } else if (format == SDL_PIXELFORMAT_ARGB1555) { pixel_format = "SDL_PIXELFORMAT_ARGB1555"; } else if (format == SDL_PIXELFORMAT_RGBA5551) { pixel_format = "SDL_PIXELFORMAT_RGBA5551"; } else if (format == SDL_PIXELFORMAT_ABGR1555) { pixel_format = "SDL_PIXELFORMAT_ABGR1555"; } else if (format == SDL_PIXELFORMAT_BGRA5551) { pixel_format = "SDL_PIXELFORMAT_BGRA5551"; } else if (format == SDL_PIXELFORMAT_RGB565) { pixel_format = "SDL_PIXELFORMAT_RGB565"; } else if (format == SDL_PIXELFORMAT_BGR565) { pixel_format = "SDL_PIXELFORMAT_BGR565"; } else if (format == SDL_PIXELFORMAT_RGB24) { pixel_format = "SDL_PIXELFORMAT_RGB24"; } else if (format == SDL_PIXELFORMAT_BGR24) { pixel_format = "SDL_PIXELFORMAT_BGR24"; } else if (format == SDL_PIXELFORMAT_RGB888) { pixel_format = "SDL_PIXELFORMAT_RGB888"; } else if (format == SDL_PIXELFORMAT_RGBX8888) { pixel_format = "SDL_PIXELFORMAT_RGBX8888"; } else if (format == SDL_PIXELFORMAT_BGR888) { pixel_format = "SDL_PIXELFORMAT_BGR888"; } else if (format == SDL_PIXELFORMAT_BGRX8888) { pixel_format = "SDL_PIXELFORMAT_BGRX8888"; } else if (format == SDL_PIXELFORMAT_ARGB8888) { pixel_format = "SDL_PIXELFORMAT_ARGB8888"; } else if (format == SDL_PIXELFORMAT_RGBA8888) { pixel_format = "SDL_PIXELFORMAT_RGBA8888"; } else if (format == SDL_PIXELFORMAT_ABGR8888) { pixel_format = "SDL_PIXELFORMAT_ABGR8888"; } else if (format == SDL_PIXELFORMAT_BGRA8888) { pixel_format = "SDL_PIXELFORMAT_BGRA8888"; } else if (format == SDL_PIXELFORMAT_ARGB2101010) { pixel_format = "SDL_PIXELFORMAT_ARGB2101010"; } return pixel_format; } SDL_Window* Game::get_window() { return window; } SDL_Renderer* Game::get_renderer() { return renderer; } void Game::run() { #if defined(__EMSCRIPTEN__) SDL_Log("using emscripten main loop"); emscripten_set_main_loop_arg(&loop, this, -1, true); #else SDL_Log("using standard main loop"); while (not done) { frame(SDL_GetTicks()); SDL_Delay(8); } #endif } void Game::frame(float ticks) { // std::cout << "frame_length: " << frame_length << ", ticks: " << ticks << ", last_frame_timestamp: " << last_frame_timestamp << // ", frame_time_overflow: " << frame_time_overflow; if (ticks - last_frame_timestamp + frame_time_overflow >= frame_length) { last_frame_length = ticks - last_frame_timestamp; if (frame_length_history.size() == 5000) { frame_length_history.pop_back(); } frame_length_history.insert(frame_length_history.begin(), last_frame_length); frame_time_overflow = last_frame_length + frame_time_overflow - frame_length; last_frame_timestamp = ticks; // std::cout << ", last_frame_length: " << last_frame_length << " [rendering frame]"; recorder.update(); delegate.dispatch(); update(); framerate_indicator.update(); if (!is_gl_context) { SDL_RenderPresent(renderer); } if (frame_time_overflow > frame_length) { // SDL_Log("%i frame(s) dropped", ((int) (frame_time_overflow / frame_length))); frame_time_overflow = 0; } frame_count_this_second++; if (ticks - last_frame_count_timestamp >= 1000) { framerate_indicator.refresh(); last_frame_count_timestamp = ticks; frame_count_this_second = 0; } } // std::cout << std::endl; } #if defined(__EMSCRIPTEN__) void loop(void* context) { Game* game = static_cast(context); game->frame(emscripten_performance_now()); if (game->done) { emscripten_cancel_main_loop(); } } #endif void Game::flag_to_end() { done = true; } glm::vec2 Game::weight(glm::vec2 motion) { return glm::vec2(weight(motion.x), weight(motion.y)); } void Game::set_framerate(int f) { if (f < 1) { f = 1; } framerate = f; frame_length = 1000.0 / framerate; } void Game::handle_quit_event(SDL_Event &event) { if (event.type == SDL_QUIT) { flag_to_end(); } } void Game::quit() { if (glcontext != NULL) { SDL_GL_DeleteContext(glcontext); } if (renderer != NULL) { SDL_DestroyRenderer(renderer); } if (window != NULL) { SDL_DestroyWindow(window); } if (TTF_WasInit()) { TTF_CloseFont(bp_mono_font); TTF_Quit(); } Mix_CloseAudio(); Mix_Quit(); SDL_Quit(); }