#include "RenderManager.h" #include #include RenderManager::RenderManager() = default; RenderManager::~RenderManager() { if (m_initialized) { shutdown(); } } bool RenderManager::initialize(int width, int height, const std::string& title) { if (m_initialized) { SDL_LogWarn(SDL_LOG_CATEGORY_RENDER, "RenderManager already initialized"); return true; } SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Initializing RenderManager (%dx%d)", width, height); // Create window m_window = SDL_CreateWindow( title.c_str(), width, height, SDL_WINDOW_RESIZABLE ); if (!m_window) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to create window: %s", SDL_GetError()); return false; } // Create renderer m_renderer = SDL_CreateRenderer(m_window, nullptr); if (!m_renderer) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Failed to create renderer: %s", SDL_GetError()); SDL_DestroyWindow(m_window); m_window = nullptr; return false; } // Enable VSync SDL_SetRenderVSync(m_renderer, 1); // Store window dimensions m_windowWidth = width; m_windowHeight = height; m_logicalWidth = width; m_logicalHeight = height; // Set initial logical size setLogicalSize(width, height); m_initialized = true; SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "RenderManager initialized successfully"); return true; } void RenderManager::shutdown() { if (!m_initialized) { return; } SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Shutting down RenderManager"); if (m_renderer) { SDL_DestroyRenderer(m_renderer); m_renderer = nullptr; } if (m_window) { SDL_DestroyWindow(m_window); m_window = nullptr; } m_initialized = false; SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "RenderManager shutdown complete"); } void RenderManager::beginFrame() { if (!m_initialized || !m_renderer) { return; } // Trace beginFrame entry { FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::beginFrame entry\n"); fclose(f); } } // Clear the screen (wrapped with trace) clear(12, 12, 16, 255); // Dark background similar to original // Trace after clear { FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::beginFrame after clear\n"); fclose(f); } } } void RenderManager::endFrame() { if (!m_initialized || !m_renderer) { return; } // Trace before present { FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::endFrame before present\n"); fclose(f); } } SDL_RenderPresent(m_renderer); // Trace after present { FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::endFrame after present\n"); fclose(f); } } } void RenderManager::setLogicalSize(int width, int height) { if (!m_initialized || !m_renderer) { return; } m_logicalWidth = width; m_logicalHeight = height; updateLogicalScale(); } void RenderManager::setViewport(int x, int y, int width, int height) { if (!m_initialized || !m_renderer) { return; } SDL_Rect viewport = { x, y, width, height }; SDL_SetRenderViewport(m_renderer, &viewport); // Keep cached logical viewport in sync if this matches our computed logical scale m_logicalVP = viewport; } void RenderManager::setScale(float scaleX, float scaleY) { if (!m_initialized || !m_renderer) { return; } m_scaleX = scaleX; m_scaleY = scaleY; SDL_SetRenderScale(m_renderer, scaleX, scaleY); } void RenderManager::resetViewport() { if (!m_initialized || !m_renderer) { return; } // Reset to full window viewport and recompute logical scale/viewport SDL_SetRenderViewport(m_renderer, nullptr); updateLogicalScale(); } void RenderManager::clear(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { if (!m_initialized || !m_renderer) { return; } SDL_SetRenderDrawColor(m_renderer, r, g, b, a); SDL_RenderClear(m_renderer); } void RenderManager::renderTexture(SDL_Texture* texture, const SDL_FRect* src, const SDL_FRect* dst) { if (!m_initialized || !m_renderer || !texture) { return; } // Trace renderTexture usage { FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::renderTexture entry tex=%llu src=%p dst=%p\n", (unsigned long long)(uintptr_t)texture, (void*)src, (void*)dst); fclose(f); } } SDL_RenderTexture(m_renderer, texture, src, dst); { FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "RenderManager::renderTexture after SDL_RenderTexture tex=%llu\n", (unsigned long long)(uintptr_t)texture); fclose(f); } } } void RenderManager::renderTexture(SDL_Texture* texture, float x, float y) { if (!texture) { return; } float w, h; SDL_GetTextureSize(texture, &w, &h); SDL_FRect dst = { x, y, w, h }; renderTexture(texture, nullptr, &dst); } void RenderManager::renderTexture(SDL_Texture* texture, float x, float y, float w, float h) { if (!texture) { return; } SDL_FRect dst = { x, y, w, h }; renderTexture(texture, nullptr, &dst); } void RenderManager::renderRect(const SDL_FRect& rect, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { if (!m_initialized || !m_renderer) { return; } SDL_SetRenderDrawColor(m_renderer, r, g, b, a); SDL_RenderFillRect(m_renderer, &rect); } void RenderManager::renderLine(float x1, float y1, float x2, float y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { if (!m_initialized || !m_renderer) { return; } SDL_SetRenderDrawColor(m_renderer, r, g, b, a); SDL_RenderLine(m_renderer, x1, y1, x2, y2); } void RenderManager::renderPoint(float x, float y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { if (!m_initialized || !m_renderer) { return; } SDL_SetRenderDrawColor(m_renderer, r, g, b, a); SDL_RenderPoint(m_renderer, x, y); } void RenderManager::handleWindowResize(int newWidth, int newHeight) { if (!m_initialized) { return; } SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "Window resized to %dx%d", newWidth, newHeight); m_windowWidth = newWidth; m_windowHeight = newHeight; updateLogicalScale(); } void RenderManager::setFullscreen(bool fullscreen) { if (!m_initialized || !m_window) { return; } if (m_isFullscreen == fullscreen) { return; } SDL_SetWindowFullscreen(m_window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0); m_isFullscreen = fullscreen; // Update window size after fullscreen change SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight); updateLogicalScale(); } void RenderManager::getWindowSize(int& width, int& height) const { width = m_windowWidth; height = m_windowHeight; } void RenderManager::getTextureSize(SDL_Texture* tex, int& w, int& h) const { if (!tex) { w = 0; h = 0; return; } // SDL3 provides SDL_GetTextureSize which accepts float or int pointers depending on overloads float fw = 0.0f, fh = 0.0f; SDL_GetTextureSize(tex, &fw, &fh); w = int(fw + 0.5f); h = int(fh + 0.5f); } void RenderManager::updateLogicalScale() { if (!m_initialized || !m_renderer) { return; } // Calculate scale to fit logical size into window float scaleX = static_cast(m_windowWidth) / static_cast(m_logicalWidth); float scaleY = static_cast(m_windowHeight) / static_cast(m_logicalHeight); // Use uniform scaling to maintain aspect ratio float scale = std::min(scaleX, scaleY); if (scale <= 0.0f) { scale = 1.0f; } setScale(scale, scale); // Compute centered logical viewport that preserves aspect ratio and is centered in window int vpW = static_cast(m_logicalWidth * scale); int vpH = static_cast(m_logicalHeight * scale); int vpX = (m_windowWidth - vpW) / 2; int vpY = (m_windowHeight - vpH) / 2; SDL_Rect vp{ vpX, vpY, vpW, vpH }; SDL_SetRenderViewport(m_renderer, &vp); // Cache logical viewport and scale for callers m_logicalVP = vp; m_logicalScale = scale; }