Skip to content

Performance Optimization

This guide covers techniques to improve game performance on ESP32, including rendering optimization, logic optimization, and profiling.

ESP32 Performance Characteristics

CPU Limitations

  • Dual-core: 240MHz (typically)
  • Single-threaded game loop: One core handles everything
  • Target FPS: 30-60 FPS (depends on game complexity)
  • Frame budget: ~16-33ms per frame at 60 FPS

Common Bottlenecks

  1. Rendering: Too many draw calls
  2. Collision detection: Too many collision checks
  3. Memory allocation: Dynamic allocation in game loop
  4. Complex calculations: Expensive math operations
  5. String operations: String concatenation/formatting

Técnicas de Optimización

El motor utiliza varias técnicas para maximizar los FPS, especialmente en hardware limitado como el ESP32.

1. Independent Resolution Scaling (Escalado de Resolución)

Esta es probablemente la optimización más impactante para el ESP32. Permite renderizar el juego a una resolución lógica menor (ej: 128x128) y reescalarla automáticamente a la resolución física de la pantalla (ej: 240x240).

  • Reducción de Memoria: Un buffer de 128x128 (8bpp) consume solo 16KB, comparado con los 57KB de uno de 240x240.
  • Aumento de FPS: Al haber menos píxeles que procesar por cada primitiva o sprite, el rendimiento puede duplicarse.
  • Implementación: Se realiza mediante Hardware-accelerated Nearest Neighbor durante la transferencia DMA.

Consulta la guía completa de Resolution Scaling para aprender a configurarlo.

2. Viewport Culling (Recorte de Cámara)

No proceses objetos que están fuera de la pantalla. El motor lo hace automáticamente en drawTileMap, pero debes implementarlo en tu lógica de actualización:

bool isOnScreen(float x, float y, int width, int height, 
                const Camera2D& camera) {
    float cameraX = camera.getX();
    float cameraY = camera.getY();
    int screenWidth = engine.getRenderer().getLogicalWidth();
    int screenHeight = engine.getRenderer().getLogicalHeight();

    return !(x + width < cameraX || 
             x > cameraX + screenWidth ||
             y + height < cameraY || 
             y > cameraY + screenHeight);
}

2. Optimización de Memoria y CPU (ESP32)

Para la plataforma ESP32, se han implementado optimizaciones de bajo nivel críticas:

  • IRAM_ATTR: Las funciones críticas de renderizado (drawSprite, drawTileMap, etc.) están marcadas para ejecutarse desde la RAM interna (IRAM), eliminando la latencia de lectura de la Flash SPI.
  • DMA (Direct Memory Access): El volcado del buffer a la pantalla TFT se realiza mediante DMA, lo que permite que la CPU comience a procesar el siguiente frame mientras el hardware transfiere los datos.
  • Acceso a Datos de 16 bits: Los sprites de 2bpp y 4bpp utilizan punteros uint16_t* para garantizar accesos alineados a memoria, lo cual es significativamente más rápido en la arquitectura Xtensa del ESP32.

3. Optimización de TileMaps

El renderizado de mapas de tiles es una de las operaciones más costosas. PixelRoot32 utiliza:

  • Caché de Paleta: Durante el dibujado de un tilemap, se genera una tabla de búsqueda (LUT) temporal para evitar cálculos de color redundantes por cada píxel.
  • Dibujado por Columnas: Optimizado para minimizar los saltos de memoria en el framebuffer.

4. Colisiones Eficientes

Usa colisiones basadas en tiles siempre que sea posible. Acceder a un array de tiles es O(1), mientras que iterar sobre una lista de entidades es O(n).

// Ejemplo de colisión rápida con el mapa
int tileX = x / 8;
int tileY = y / 8;
if (levelMap.data[tileY * levelMap.width + tileX] != 0) {
    // Colisión detectada
}

Recomendaciones Generales

  • Sprites Indexados: Prefiere Sprite2bpp (4 colores) o Sprite4bpp (16 colores) sobre Sprite (1bpp) si necesitas color, ya que están altamente optimizados.
  • Evitar std::string en el Loop: Las concatenaciones de strings generan fragmentación de memoria. Usa buffers estáticos o char[] para textos dinámicos.
  • Perfilado: Activa el overlay de estadísticas de depuración compilando con PIXELROOT32_ENABLE_DEBUG_OVERLAY (ver Engine - Debug Overlay) para monitorear FPS, RAM y carga de CPU en tiempo real.

Common Optimization Patterns

Update Frequency Reduction

class LowFrequencyUpdater {
private:
    unsigned long timer = 0;
    unsigned long interval = 100; // Update every 100ms

public:
    void update(unsigned long deltaTime) {
        timer += deltaTime;
        if (timer >= interval) {
            timer -= interval;
            // Do expensive update
            expensiveUpdate();
        }
    }
};

Spatial Partitioning (Simple)

// Divide screen into zones
class SpatialGrid {
private:
    static const int GRID_SIZE = 4;
    static const int CELL_WIDTH = 60;
    static const int CELL_HEIGHT = 60;

    std::vector<Actor*> grid[GRID_SIZE][GRID_SIZE];

public:
    void add(Actor* actor) {
        int cellX = static_cast<int>(actor->x) / CELL_WIDTH;
        int cellY = static_cast<int>(actor->y) / CELL_HEIGHT;
        if (cellX >= 0 && cellX < GRID_SIZE && 
            cellY >= 0 && cellY < GRID_SIZE) {
            grid[cellY][cellX].push_back(actor);
        }
    }

    void checkCollisions() {
        // Only check collisions within same cell
        for (int y = 0; y < GRID_SIZE; y++) {
            for (int x = 0; x < GRID_SIZE; x++) {
                auto& cell = grid[y][x];
                for (size_t i = 0; i < cell.size(); i++) {
                    for (size_t j = i + 1; j < cell.size(); j++) {
                        checkCollision(cell[i], cell[j]);
                    }
                }
            }
        }
    }
};

Troubleshooting

Low FPS

  • Profile to find bottlenecks
  • Reduce entity count
  • Optimize rendering (culling, batching)
  • Simplify collision detection
  • Reduce update frequency

Frame Drops

  • Check for expensive operations in update()
  • Avoid dynamic allocation
  • Cache calculations
  • Reduce draw calls

Stuttering

  • Ensure frame-rate independence (use deltaTime)
  • Avoid blocking operations
  • Pre-load resources
  • Use object pooling

Next Steps

Now that you understand performance optimization, learn about: - Memory Management - Manage memory efficiently - Platforms and Drivers - Platform-specific optimizations - Extensibility - Extend the engine


See also: - Manual - Basic Rendering - Manual - Physics and Collisions