#include #include #include #include #include #include namespace Game::Renderer { Renderer::Renderer() : mRenderer(nullptr) {} Renderer::~Renderer() { destroy(); } void Renderer::destroy() { if (mRenderer) { SDL_DestroyRenderer(mRenderer); mRenderer = nullptr; LOG("Destroyed Renderer"); } } bool Renderer::init(SDL_Window* window) { // Request VSync before/at renderer setup; some backends honor this hint. SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); mRenderer = SDL_CreateRenderer(window, nullptr); if (!mRenderer) { std::string errorMsg = "Failed to create renderer: " + std::string(SDL_GetError()); ERROR(errorMsg.c_str()); return false; } mVSyncEnabled = SDL_SetRenderVSync(mRenderer, 1); if (!mVSyncEnabled) { WARN("VSync could not be enabled, using software frame pacing fallback: " << SDL_GetError()); } if (!SDL_SetRenderDrawColor(mRenderer, 0, 0, 255, 255)) { ERROR("Failed to set renderer draw color: " << SDL_GetError()); return false; } LOG("Renderer created successfully"); return true; } void Renderer::renderFrame() { mClear(); float camX, camY; Object::Camera::getInstance().getPosition(camX, camY); int screenW, screenH; SDL_GetWindowSizeInPixels(Window::Window::getSDLWindowBackend(), &screenW, &screenH); // Pass the config to avoid wasting time recalculating it for every entity, since it's not gonna change during the frame RendererConfig config{ camX, camY, screenW, screenH }; try { auto entities = Game::State::GameState::getInstance().getEntitiesSnapshot(true); for (auto* entity : entities) { if (entity) { entity->render(this, config); } } } catch (const std::exception& e) { ERROR("Exception while rendering frame: " << e.what()); } mPresent(); } void Renderer::mClear() { if (!SDL_RenderClear(mRenderer)) { ERROR("Failed to clear renderer: " << SDL_GetError()); } } void Renderer::mPresent() { if (!SDL_RenderPresent(mRenderer)) { ERROR("Failed to present renderer: " << SDL_GetError()); } } }