Add comprehensive performance optimization guide for Canvas and mobile development
This commit is contained in:
224
knowledge-base/technical/performance-optimization.md
Normal file
224
knowledge-base/technical/performance-optimization.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# Performance Optimization Guide
|
||||
|
||||
## Current Performance Targets
|
||||
|
||||
### Frame Rate Goals
|
||||
- **Desktop**: Consistent 60fps on mid-range hardware (2019+)
|
||||
- **Mobile**: Minimum 30fps on devices from 2020+
|
||||
- **Tablet**: 45-60fps for smooth touch interaction
|
||||
- **Degradation Strategy**: Reduce particle effects before dropping frame rate
|
||||
|
||||
### Memory Usage Targets
|
||||
- **Desktop**: <100MB during gameplay
|
||||
- **Mobile**: <50MB during gameplay
|
||||
- **Peak Usage**: <150MB during intensive scenes (level completion effects)
|
||||
|
||||
### Loading Performance
|
||||
- **Initial Load**: <3 seconds on 3G connection
|
||||
- **Level Transition**: <500ms between levels
|
||||
- **Asset Loading**: Progressive where possible
|
||||
|
||||
## Canvas Optimization Techniques
|
||||
|
||||
### Efficient Rendering
|
||||
```javascript
|
||||
// Use requestAnimationFrame for smooth animation
|
||||
function gameLoop() {
|
||||
// Limit updates to 60fps maximum
|
||||
requestAnimationFrame(gameLoop);
|
||||
|
||||
// Only redraw if something changed
|
||||
if (gameState.needsRedraw) {
|
||||
render();
|
||||
gameState.needsRedraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Efficient canvas clearing
|
||||
function clearCanvas() {
|
||||
// Fastest method for full clear
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
```
|
||||
|
||||
### Drawing Optimization
|
||||
```javascript
|
||||
// Batch similar drawing operations
|
||||
function drawConnections() {
|
||||
ctx.lineWidth = 3;
|
||||
|
||||
gameState.connections.forEach(connection => {
|
||||
const gradient = ctx.createLinearGradient(
|
||||
connection.nodeA.x, connection.nodeA.y,
|
||||
connection.nodeB.x, connection.nodeB.y
|
||||
);
|
||||
gradient.addColorStop(0, '#00d4ff');
|
||||
gradient.addColorStop(0.5, '#ff00ff');
|
||||
gradient.addColorStop(1, '#00d4ff');
|
||||
|
||||
ctx.strokeStyle = gradient;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(connection.nodeA.x, connection.nodeA.y);
|
||||
ctx.lineTo(connection.nodeB.x, connection.nodeB.y);
|
||||
ctx.stroke();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Memory Management
|
||||
|
||||
### Object Pooling for Particles
|
||||
```javascript
|
||||
class ParticlePool {
|
||||
constructor(initialSize = 100) {
|
||||
this.pool = [];
|
||||
this.active = [];
|
||||
|
||||
// Pre-create objects
|
||||
for (let i = 0; i < initialSize; i++) {
|
||||
this.pool.push(new Particle());
|
||||
}
|
||||
}
|
||||
|
||||
acquire() {
|
||||
let particle = this.pool.pop();
|
||||
if (!particle) {
|
||||
particle = new Particle();
|
||||
}
|
||||
this.active.push(particle);
|
||||
return particle;
|
||||
}
|
||||
|
||||
release(particle) {
|
||||
const index = this.active.indexOf(particle);
|
||||
if (index !== -1) {
|
||||
this.active.splice(index, 1);
|
||||
particle.reset();
|
||||
this.pool.push(particle);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Monitoring
|
||||
|
||||
### Frame Rate Monitoring
|
||||
```javascript
|
||||
class PerformanceMonitor {
|
||||
constructor() {
|
||||
this.frameTimes = [];
|
||||
this.lastTime = performance.now();
|
||||
this.frameCount = 0;
|
||||
}
|
||||
|
||||
update() {
|
||||
const currentTime = performance.now();
|
||||
const frameTime = currentTime - this.lastTime;
|
||||
this.lastTime = currentTime;
|
||||
|
||||
this.frameTimes.push(frameTime);
|
||||
if (this.frameTimes.length > 60) {
|
||||
this.frameTimes.shift();
|
||||
}
|
||||
|
||||
this.frameCount++;
|
||||
|
||||
// Log performance every 5 seconds
|
||||
if (this.frameCount % 300 === 0) {
|
||||
this.logPerformance();
|
||||
}
|
||||
}
|
||||
|
||||
logPerformance() {
|
||||
const avgFrameTime = this.frameTimes.reduce((a, b) => a + b) / this.frameTimes.length;
|
||||
const fps = 1000 / avgFrameTime;
|
||||
|
||||
console.log(`Performance: ${fps.toFixed(1)} fps (${avgFrameTime.toFixed(1)}ms avg frame time)`);
|
||||
|
||||
if (performance.memory) {
|
||||
const memory = performance.memory.usedJSHeapSize / 1024 / 1024;
|
||||
console.log(`Memory usage: ${memory.toFixed(1)} MB`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Leak Prevention
|
||||
```javascript
|
||||
// Proper cleanup patterns
|
||||
function cleanupLevel() {
|
||||
// Remove all event listeners
|
||||
gameState.nodes.forEach(node => {
|
||||
if (node.cleanup) node.cleanup();
|
||||
});
|
||||
|
||||
// Clear arrays properly
|
||||
gameState.nodes.length = 0;
|
||||
gameState.connections.length = 0;
|
||||
gameState.particles.length = 0;
|
||||
|
||||
// Clear any timers
|
||||
clearInterval(gameState.timerId);
|
||||
gameState.timerId = null;
|
||||
}
|
||||
```
|
||||
|
||||
## Mobile Optimizations
|
||||
|
||||
### Touch Performance
|
||||
```javascript
|
||||
// Throttle touch events to 60fps max
|
||||
const throttledTouchMove = throttle((e) => {
|
||||
e.preventDefault();
|
||||
handleTouchMove(e);
|
||||
}, 16);
|
||||
|
||||
canvas.addEventListener('touchmove', throttledTouchMove, {passive: false});
|
||||
```
|
||||
|
||||
### Battery Optimization
|
||||
```javascript
|
||||
// Reduce frame rate when tab is not visible
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.hidden) {
|
||||
gameState.targetFPS = 15; // Reduce to save battery
|
||||
} else {
|
||||
gameState.targetFPS = 60; // Resume full speed
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Performance Testing Checklist
|
||||
|
||||
### Manual Testing
|
||||
- [ ] 60fps on desktop Chrome/Firefox/Safari
|
||||
- [ ] 30fps+ on iPhone 12 Safari
|
||||
- [ ] 30fps+ on Android Chrome (mid-range device)
|
||||
- [ ] No frame drops during particle effects
|
||||
- [ ] Smooth level transitions
|
||||
- [ ] Memory usage stable over 20+ levels
|
||||
|
||||
### Automated Monitoring
|
||||
```javascript
|
||||
// Performance testing helper
|
||||
if (location.hostname === 'localhost') {
|
||||
window.performanceTest = {
|
||||
monitor: new PerformanceMonitor(),
|
||||
logMemory: () => {
|
||||
if (performance.memory) {
|
||||
const used = Math.round(performance.memory.usedJSHeapSize / 1024 / 1024);
|
||||
const total = Math.round(performance.memory.totalJSHeapSize / 1024 / 1024);
|
||||
console.log(`Memory: ${used}MB / ${total}MB`);
|
||||
}
|
||||
},
|
||||
startProfiling: () => {
|
||||
console.profile('Game Performance');
|
||||
},
|
||||
stopProfiling: () => {
|
||||
console.profileEnd('Game Performance');
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
This guide should be updated as new optimization techniques are discovered and performance targets evolve.
|
||||
Reference in New Issue
Block a user