第14.3章:着色器性能分析

性能分析是着色器优化的基础。本教程将深入讲解如何系统性地分析着色器性能,识别瓶颈并制定优化策略。

🎯 学习目标

  • 掌握着色器性能分析的方法和工具
  • 学会识别和定位性能瓶颈
  • 理解GPU性能指标和优化目�?- 建立完整的性能监控体系

📊 性能分析基础

GPU性能指标体系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// GPU性能分析�?class GPUPerformanceProfiler {
private gl: WebGLRenderingContext | WebGL2RenderingContext;
private queryExtension: any;
private frameMetrics: FrameMetrics[] = [];
private shaderMetrics: Map<string, ShaderMetrics> = new Map();

interface FrameMetrics {
frameNumber: number;
frameTime: number;
gpuTime: number;
drawCalls: number;
triangles: number;
vertices: number;
textureBinds: number;
stateChanges: number;
shaderSwitches: number;
overdrawRatio: number;
fillrate: number;
memoryUsage: MemoryUsage;
timestamp: number;
}

interface ShaderMetrics {
name: string;
compileTime: number;
complexity: ShaderComplexity;
usage: ShaderUsage;
performance: ShaderPerformance;
optimizationSuggestions: string[];
}

interface ShaderComplexity {
vertexInstructions: number;
fragmentInstructions: number;
textureReads: number;
mathOperations: number;
branchingComplexity: number;
registerPressure: number;
}

interface ShaderUsage {
timesUsed: number;
totalPixelsShaded: number;
totalVerticesProcessed: number;
averageInstanceCount: number;
}

interface ShaderPerformance {
averageGPUTime: number;
peakGPUTime: number;
averageFillrate: number;
bottleneckType: 'vertex' | 'fragment' | 'bandwidth' | 'none';
efficiency: number; // 0-1
}

interface MemoryUsage {
totalAllocated: number;
textureMemory: number;
bufferMemory: number;
framebufferMemory: number;
availableMemory: number;
}

constructor(gl: WebGLRenderingContext | WebGL2RenderingContext) {
this.gl = gl;
this.initializeProfiler();
}

private initializeProfiler(): void {
// 尝试获取时间查询扩展
this.queryExtension = this.gl.getExtension('EXT_disjoint_timer_query_webgl2') ||
this.gl.getExtension('EXT_disjoint_timer_query');

if (this.queryExtension) {
console.log('�?GPU时间查询扩展可用');
} else {
console.warn('⚠️ GPU时间查询扩展不可用,将使用替代方�?);
}

this.startFrameMonitoring();
}

private startFrameMonitoring(): void {
let frameCount = 0;
const startTime = performance.now();

const measureFrame = () => {
const currentTime = performance.now();
const frameTime = currentTime - (this.lastFrameTime || currentTime);
this.lastFrameTime = currentTime;

// 收集帧指�? this.collectFrameMetrics(frameCount++, frameTime);

requestAnimationFrame(measureFrame);
};

requestAnimationFrame(measureFrame);
}

private collectFrameMetrics(frameNumber: number, frameTime: number): void {
const metrics: FrameMetrics = {
frameNumber: frameNumber,
frameTime: frameTime,
gpuTime: this.measureGPUTime(),
drawCalls: this.getDrawCallCount(),
triangles: this.getTriangleCount(),
vertices: this.getVertexCount(),
textureBinds: this.getTextureBindCount(),
stateChanges: this.getStateChangeCount(),
shaderSwitches: this.getShaderSwitchCount(),
overdrawRatio: this.measureOverdraw(),
fillrate: this.calculateFillrate(),
memoryUsage: this.getMemoryUsage(),
timestamp: performance.now()
};

this.frameMetrics.push(metrics);

// 保持最�?00帧的数据
if (this.frameMetrics.length > 100) {
this.frameMetrics.shift();
}

// �?0帧分析一次性能
if (frameNumber % 30 === 0) {
this.analyzePerformance();
}
}

private measureGPUTime(): number {
if (!this.queryExtension) {
return -1; // 无法测量
}

// 这里实现GPU时间查询
// WebGL的时间查询是异步的,需要特殊处�? return 0; // 简化示�? }

private measureOverdraw(): number {
// 通过渲染统计估算overdraw
const canvas = this.gl.canvas as HTMLCanvasElement;
const totalPixels = canvas.width * canvas.height;
const shadedPixels = this.estimateShadedPixels();

return shadedPixels / totalPixels;
}

private calculateFillrate(): number {
// 计算填充�?(像素/�?
const canvas = this.gl.canvas as HTMLCanvasElement;
const pixelsPerFrame = canvas.width * canvas.height * this.measureOverdraw();
const frameTime = this.getAverageFrameTime();

return frameTime > 0 ? (pixelsPerFrame / frameTime) * 1000 : 0;
}

private analyzePerformance(): void {
const recentMetrics = this.frameMetrics.slice(-30);
const analysis = this.performBottleneckAnalysis(recentMetrics);

if (analysis.bottleneck !== 'none') {
console.log(`⚠️ 性能瓶颈检�? ${analysis.bottleneck}`);
console.log('🔧 优化建议:', analysis.suggestions);
}
}

private performBottleneckAnalysis(metrics: FrameMetrics[]): BottleneckAnalysis {
const avgGPUTime = metrics.reduce((sum, m) => sum + m.gpuTime, 0) / metrics.length;
const avgFrameTime = metrics.reduce((sum, m) => sum + m.frameTime, 0) / metrics.length;
const avgOverdraw = metrics.reduce((sum, m) => sum + m.overdrawRatio, 0) / metrics.length;
const avgDrawCalls = metrics.reduce((sum, m) => sum + m.drawCalls, 0) / metrics.length;

let bottleneck: 'cpu' | 'gpu' | 'bandwidth' | 'fillrate' | 'drawcalls' | 'none' = 'none';
const suggestions: string[] = [];

// GPU限制分析
if (avgGPUTime > avgFrameTime * 0.8) {
if (avgOverdraw > 3.0) {
bottleneck = 'fillrate';
suggestions.push('减少overdraw,优化深度测�?);
suggestions.push('简化fragment shader复杂�?);
} else {
bottleneck = 'gpu';
suggestions.push('优化着色器算法复杂�?);
suggestions.push('减少纹理采样次数');
}
}

// CPU限制分析
if (avgFrameTime - avgGPUTime > avgFrameTime * 0.3) {
bottleneck = 'cpu';
suggestions.push('减少draw call数量');
suggestions.push('优化状态切�?);
}

// Draw call瓶颈
if (avgDrawCalls > 1000) {
bottleneck = 'drawcalls';
suggestions.push('使用实例化渲�?);
suggestions.push('合并渲染批次');
}

return { bottleneck, suggestions };
}

public generatePerformanceReport(): PerformanceReport {
const recentMetrics = this.frameMetrics.slice(-60); // 最�?0�?
if (recentMetrics.length === 0) {
return this.getEmptyReport();
}

const report: PerformanceReport = {
summary: this.generateSummary(recentMetrics),
frameAnalysis: this.analyzeFrames(recentMetrics),
shaderAnalysis: this.analyzeShaders(),
memoryAnalysis: this.analyzeMemory(recentMetrics),
recommendations: this.generateRecommendations(recentMetrics),
timestamp: Date.now()
};

return report;
}

private generateSummary(metrics: FrameMetrics[]): PerformanceSummary {
const frameTime = metrics.map(m => m.frameTime);
const gpuTime = metrics.map(m => m.gpuTime).filter(t => t >= 0);

return {
averageFPS: 1000 / (frameTime.reduce((a, b) => a + b) / frameTime.length),
averageFrameTime: frameTime.reduce((a, b) => a + b) / frameTime.length,
averageGPUTime: gpuTime.length > 0 ? gpuTime.reduce((a, b) => a + b) / gpuTime.length : -1,
minFPS: 1000 / Math.max(...frameTime),
maxFPS: 1000 / Math.min(...frameTime),
frameTimeVariance: this.calculateVariance(frameTime),
averageDrawCalls: metrics.reduce((sum, m) => sum + m.drawCalls, 0) / metrics.length,
averageTriangles: metrics.reduce((sum, m) => sum + m.triangles, 0) / metrics.length,
averageOverdraw: metrics.reduce((sum, m) => sum + m.overdrawRatio, 0) / metrics.length
};
}

public startShaderProfiling(shaderName: string): void {
const startTime = performance.now();

// 记录着色器开始使�? if (!this.shaderMetrics.has(shaderName)) {
this.shaderMetrics.set(shaderName, {
name: shaderName,
compileTime: 0,
complexity: this.analyzeShaderComplexity(shaderName),
usage: { timesUsed: 0, totalPixelsShaded: 0, totalVerticesProcessed: 0, averageInstanceCount: 0 },
performance: { averageGPUTime: 0, peakGPUTime: 0, averageFillrate: 0, bottleneckType: 'none', efficiency: 1.0 },
optimizationSuggestions: []
});
}

const metrics = this.shaderMetrics.get(shaderName)!;
metrics.usage.timesUsed++;
}

private analyzeShaderComplexity(shaderName: string): ShaderComplexity {
// 这里需要实际的着色器源码分析
// 简化示�? return {
vertexInstructions: 50,
fragmentInstructions: 100,
textureReads: 3,
mathOperations: 20,
branchingComplexity: 2,
registerPressure: 0.6
};
}
}

interface BottleneckAnalysis {
bottleneck: 'cpu' | 'gpu' | 'bandwidth' | 'fillrate' | 'drawcalls' | 'none';
suggestions: string[];
}

interface PerformanceReport {
summary: PerformanceSummary;
frameAnalysis: FrameAnalysis;
shaderAnalysis: ShaderAnalysisReport;
memoryAnalysis: MemoryAnalysisReport;
recommendations: string[];
timestamp: number;
}

interface PerformanceSummary {
averageFPS: number;
averageFrameTime: number;
averageGPUTime: number;
minFPS: number;
maxFPS: number;
frameTimeVariance: number;
averageDrawCalls: number;
averageTriangles: number;
averageOverdraw: number;
}

🔍 着色器复杂度分�?

着色器指令分析�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// 着色器指令分析�?class ShaderInstructionAnalyzer {
private instructionCosts: Map<string, InstructionCost> = new Map();

interface InstructionCost {
aluCost: number;
textureCost: number;
bandwidthCost: number;
registerPressure: number;
category: 'math' | 'texture' | 'control' | 'memory';
}

interface ShaderAnalysisResult {
totalInstructions: number;
aluInstructions: number;
textureInstructions: number;
controlInstructions: number;
estimatedCycles: number;
registerUsage: number;
complexityScore: number;
optimizationOpportunities: OptimizationOpportunity[];
}

interface OptimizationOpportunity {
type: 'reduce_alu' | 'reduce_texture' | 'reduce_branching' | 'reduce_precision' | 'vectorize';
description: string;
impact: 'high' | 'medium' | 'low';
implementation: string;
}

constructor() {
this.initializeInstructionCosts();
}

private initializeInstructionCosts(): void {
// ALU指令成本
this.instructionCosts.set('add', { aluCost: 1, textureCost: 0, bandwidthCost: 0, registerPressure: 0.1, category: 'math' });
this.instructionCosts.set('mul', { aluCost: 1, textureCost: 0, bandwidthCost: 0, registerPressure: 0.1, category: 'math' });
this.instructionCosts.set('div', { aluCost: 4, textureCost: 0, bandwidthCost: 0, registerPressure: 0.2, category: 'math' });
this.instructionCosts.set('sqrt', { aluCost: 3, textureCost: 0, bandwidthCost: 0, registerPressure: 0.2, category: 'math' });
this.instructionCosts.set('sin', { aluCost: 8, textureCost: 0, bandwidthCost: 0, registerPressure: 0.3, category: 'math' });
this.instructionCosts.set('cos', { aluCost: 8, textureCost: 0, bandwidthCost: 0, registerPressure: 0.3, category: 'math' });
this.instructionCosts.set('pow', { aluCost: 12, textureCost: 0, bandwidthCost: 0, registerPressure: 0.4, category: 'math' });

// 纹理指令成本
this.instructionCosts.set('texture2D', { aluCost: 1, textureCost: 4, bandwidthCost: 8, registerPressure: 0.2, category: 'texture' });
this.instructionCosts.set('textureCube', { aluCost: 1, textureCost: 6, bandwidthCost: 12, registerPressure: 0.3, category: 'texture' });
this.instructionCosts.set('texture2DLod', { aluCost: 2, textureCost: 6, bandwidthCost: 10, registerPressure: 0.3, category: 'texture' });

// 控制流指令成�? this.instructionCosts.set('if', { aluCost: 2, textureCost: 0, bandwidthCost: 0, registerPressure: 0.1, category: 'control' });
this.instructionCosts.set('for', { aluCost: 3, textureCost: 0, bandwidthCost: 0, registerPressure: 0.2, category: 'control' });
this.instructionCosts.set('discard', { aluCost: 5, textureCost: 0, bandwidthCost: 0, registerPressure: 0.1, category: 'control' });
}

public analyzeShaderSource(vertexSource: string, fragmentSource: string): ShaderAnalysisResult {
console.log('🔍 分析着色器复杂�?..');

const vertexAnalysis = this.analyzeShaderStage(vertexSource, 'vertex');
const fragmentAnalysis = this.analyzeShaderStage(fragmentSource, 'fragment');

// 合并分析结果
const result: ShaderAnalysisResult = {
totalInstructions: vertexAnalysis.instructions + fragmentAnalysis.instructions,
aluInstructions: vertexAnalysis.aluInstructions + fragmentAnalysis.aluInstructions,
textureInstructions: vertexAnalysis.textureInstructions + fragmentAnalysis.textureInstructions,
controlInstructions: vertexAnalysis.controlInstructions + fragmentAnalysis.controlInstructions,
estimatedCycles: Math.max(vertexAnalysis.estimatedCycles, fragmentAnalysis.estimatedCycles), // 取最大�? registerUsage: Math.max(vertexAnalysis.registerUsage, fragmentAnalysis.registerUsage),
complexityScore: this.calculateComplexityScore(vertexAnalysis, fragmentAnalysis),
optimizationOpportunities: [
...vertexAnalysis.optimizations,
...fragmentAnalysis.optimizations
]
};

console.log('📊 着色器分析结果:', result);
return result;
}

private analyzeShaderStage(source: string, stage: 'vertex' | 'fragment'): StageAnalysis {
const lines = source.split('\n');
let instructions = 0;
let aluInstructions = 0;
let textureInstructions = 0;
let controlInstructions = 0;
let estimatedCycles = 0;
let registerUsage = 0;
const optimizations: OptimizationOpportunity[] = [];

// 简化的指令分析
for (const line of lines) {
const trimmedLine = line.trim();

// 跳过注释和空�? if (trimmedLine.startsWith('//') || trimmedLine.length === 0) continue;

instructions++;

// 分析指令类型
if (this.containsTextureOperation(trimmedLine)) {
textureInstructions++;
estimatedCycles += 4;
registerUsage += 0.2;

// 检查纹理优化机�? if (this.hasTextureOptimizationOpportunity(trimmedLine)) {
optimizations.push({
type: 'reduce_texture',
description: '可以合并或减少纹理采�?,
impact: 'medium',
implementation: '使用atlas纹理或减少采样次�?
});
}
}

if (this.containsMathOperation(trimmedLine)) {
aluInstructions++;
estimatedCycles += this.estimateMathCycles(trimmedLine);
registerUsage += 0.1;

// 检查数学优化机�? if (this.hasMathOptimizationOpportunity(trimmedLine)) {
optimizations.push({
type: 'reduce_alu',
description: '可以简化数学计�?,
impact: 'medium',
implementation: '使用查找表或简化算�?
});
}
}

if (this.containsControlFlow(trimmedLine)) {
controlInstructions++;
estimatedCycles += 2;
registerUsage += 0.15;

// 检查分支优化机�? optimizations.push({
type: 'reduce_branching',
description: '可以减少条件分支',
impact: 'high',
implementation: '使用step/lerp函数替代if语句'
});
}
}

return {
instructions,
aluInstructions,
textureInstructions,
controlInstructions,
estimatedCycles,
registerUsage,
optimizations
};
}

private containsTextureOperation(line: string): boolean {
return /texture2D|textureCube|texture2DLod|texture2DProj/.test(line);
}

private containsMathOperation(line: string): boolean {
return /\+|\-|\*|\/|pow|sqrt|sin|cos|tan|normalize|dot|cross|length/.test(line);
}

private containsControlFlow(line: string): boolean {
return /\bif\b|\bfor\b|\bwhile\b|\bdiscard\b/.test(line);
}

private estimateMathCycles(line: string): number {
let cycles = 1; // 基础成本

if (line.includes('pow')) cycles += 12;
else if (line.includes('sin') || line.includes('cos')) cycles += 8;
else if (line.includes('sqrt')) cycles += 3;
else if (line.includes('/')) cycles += 4;
else if (line.includes('normalize')) cycles += 4;

return cycles;
}

private calculateComplexityScore(vertexAnalysis: StageAnalysis, fragmentAnalysis: StageAnalysis): number {
// 复杂度评�?(0-100)
const vertexScore = vertexAnalysis.estimatedCycles / 10;
const fragmentScore = fragmentAnalysis.estimatedCycles / 20; // fragment更重�?
return Math.min(100, vertexScore + fragmentScore);
}

public generateOptimizationReport(analysis: ShaderAnalysisResult): string {
let report = '🔍 着色器性能分析报告\n';
report += '=' * 40 + '\n';
report += `总指令数: ${analysis.totalInstructions}\n`;
report += `ALU指令: ${analysis.aluInstructions}\n`;
report += `纹理指令: ${analysis.textureInstructions}\n`;
report += `控制流指�? ${analysis.controlInstructions}\n`;
report += `预估周期�? ${analysis.estimatedCycles}\n`;
report += `寄存器使用率: ${(analysis.registerUsage * 100).toFixed(1)}%\n`;
report += `复杂度评�? ${analysis.complexityScore.toFixed(1)}/100\n\n`;

if (analysis.complexityScore > 70) {
report += '⚠️ 着色器复杂度较高,建议优化\n\n';
} else if (analysis.complexityScore > 50) {
report += '💡 着色器复杂度中等,可考虑优化\n\n';
} else {
report += '�?着色器复杂度良好\n\n';
}

if (analysis.optimizationOpportunities.length > 0) {
report += '🔧 优化建议:\n';
analysis.optimizationOpportunities.forEach((opt, index) => {
report += `${index + 1}. [${opt.impact.toUpperCase()}] ${opt.description}\n`;
report += ` 实现方式: ${opt.implementation}\n\n`;
});
}

return report;
}
}

interface StageAnalysis {
instructions: number;
aluInstructions: number;
textureInstructions: number;
controlInstructions: number;
estimatedCycles: number;
registerUsage: number;
optimizations: OptimizationOpportunity[];
}

📊 实时性能监控

性能监控仪表�?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
// 实时性能监控仪表�?class PerformanceDashboard {
private profiler: GPUPerformanceProfiler;
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
private charts: Map<string, PerformanceChart> = new Map();
private isVisible: boolean = false;

interface PerformanceChart {
name: string;
data: number[];
maxDataPoints: number;
color: string;
unit: string;
range: { min: number; max: number };
}

constructor(profiler: GPUPerformanceProfiler) {
this.profiler = profiler;
this.createDashboard();
this.initializeCharts();
this.startRealTimeUpdates();
}

private createDashboard(): void {
// 创建仪表板DOM元素
const dashboard = document.createElement('div');
dashboard.id = 'performance-dashboard';
dashboard.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
width: 400px;
height: 600px;
background: rgba(0, 0, 0, 0.8);
color: white;
font-family: monospace;
font-size: 12px;
padding: 10px;
border-radius: 5px;
z-index: 10000;
display: none;
`;

// 创建画布
this.canvas = document.createElement('canvas');
this.canvas.width = 380;
this.canvas.height = 580;
this.ctx = this.canvas.getContext('2d')!;

dashboard.appendChild(this.canvas);
document.body.appendChild(dashboard);

// 添加切换按钮
const toggleButton = document.createElement('button');
toggleButton.textContent = 'Performance';
toggleButton.style.cssText = `
position: fixed;
top: 10px;
right: 420px;
z-index: 10001;
padding: 5px 10px;
background: #333;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
`;

toggleButton.onclick = () => this.toggleVisibility();
document.body.appendChild(toggleButton);
}

private initializeCharts(): void {
this.charts.set('fps', {
name: 'FPS',
data: [],
maxDataPoints: 100,
color: '#00ff00',
unit: 'fps',
range: { min: 0, max: 120 }
});

this.charts.set('frameTime', {
name: 'Frame Time',
data: [],
maxDataPoints: 100,
color: '#ffff00',
unit: 'ms',
range: { min: 0, max: 50 }
});

this.charts.set('gpuTime', {
name: 'GPU Time',
data: [],
maxDataPoints: 100,
color: '#ff8800',
unit: 'ms',
range: { min: 0, max: 30 }
});

this.charts.set('drawCalls', {
name: 'Draw Calls',
data: [],
maxDataPoints: 100,
color: '#ff0088',
unit: 'calls',
range: { min: 0, max: 1000 }
});

this.charts.set('triangles', {
name: 'Triangles',
data: [],
maxDataPoints: 100,
color: '#8800ff',
unit: 'k',
range: { min: 0, max: 100 }
});

this.charts.set('overdraw', {
name: 'Overdraw',
data: [],
maxDataPoints: 100,
color: '#00ffff',
unit: 'x',
range: { min: 1, max: 5 }
});
}

private startRealTimeUpdates(): void {
setInterval(() => {
if (this.isVisible) {
this.updateCharts();
this.drawDashboard();
}
}, 100); // 10fps更新�? }

private updateCharts(): void {
const report = this.profiler.generatePerformanceReport();
const summary = report.summary;

// 更新图表数据
this.addDataPoint('fps', summary.averageFPS);
this.addDataPoint('frameTime', summary.averageFrameTime);
this.addDataPoint('gpuTime', summary.averageGPUTime > 0 ? summary.averageGPUTime : 0);
this.addDataPoint('drawCalls', summary.averageDrawCalls);
this.addDataPoint('triangles', summary.averageTriangles / 1000);
this.addDataPoint('overdraw', summary.averageOverdraw);
}

private addDataPoint(chartName: string, value: number): void {
const chart = this.charts.get(chartName);
if (chart) {
chart.data.push(value);
if (chart.data.length > chart.maxDataPoints) {
chart.data.shift();
}
}
}

private drawDashboard(): void {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

// 绘制标题
this.ctx.fillStyle = '#ffffff';
this.ctx.font = '16px monospace';
this.ctx.fillText('Performance Dashboard', 10, 25);

// 绘制图表
let yOffset = 50;
const chartHeight = 80;
const chartSpacing = 90;

for (const [name, chart] of this.charts.entries()) {
this.drawChart(chart, 10, yOffset, 360, chartHeight);
yOffset += chartSpacing;
}

// 绘制实时统计
this.drawRealTimeStats(10, yOffset);
}

private drawChart(chart: PerformanceChart, x: number, y: number, width: number, height: number): void {
// 绘制图表背景
this.ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
this.ctx.fillRect(x, y, width, height);

// 绘制图表标题
this.ctx.fillStyle = chart.color;
this.ctx.font = '12px monospace';
this.ctx.fillText(`${chart.name} (${chart.unit})`, x + 5, y - 5);

// 绘制当前�? const currentValue = chart.data[chart.data.length - 1] || 0;
this.ctx.fillText(`${currentValue.toFixed(1)}`, x + width - 50, y - 5);

// 绘制数据�? if (chart.data.length > 1) {
this.ctx.strokeStyle = chart.color;
this.ctx.lineWidth = 2;
this.ctx.beginPath();

for (let i = 0; i < chart.data.length; i++) {
const dataX = x + (i / (chart.maxDataPoints - 1)) * width;
const normalizedValue = (chart.data[i] - chart.range.min) / (chart.range.max - chart.range.min);
const dataY = y + height - (normalizedValue * height);

if (i === 0) {
this.ctx.moveTo(dataX, dataY);
} else {
this.ctx.lineTo(dataX, dataY);
}
}

this.ctx.stroke();
}

// 绘制网格�? this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
this.ctx.lineWidth = 1;
for (let i = 1; i < 4; i++) {
const gridY = y + (i / 4) * height;
this.ctx.beginPath();
this.ctx.moveTo(x, gridY);
this.ctx.lineTo(x + width, gridY);
this.ctx.stroke();
}
}

private drawRealTimeStats(x: number, y: number): void {
const report = this.profiler.generatePerformanceReport();

this.ctx.fillStyle = '#ffffff';
this.ctx.font = '11px monospace';

const stats = [
`Min FPS: ${report.summary.minFPS.toFixed(1)}`,
`Max FPS: ${report.summary.maxFPS.toFixed(1)}`,
`Frame Variance: ${report.summary.frameTimeVariance.toFixed(2)}ms`,
`Memory Usage: ${this.formatMemorySize(report.memoryAnalysis?.totalUsed || 0)}`,
`Texture Memory: ${this.formatMemorySize(report.memoryAnalysis?.textureMemory || 0)}`
];

stats.forEach((stat, index) => {
this.ctx.fillText(stat, x, y + index * 15);
});
}

private formatMemorySize(bytes: number): string {
if (bytes < 1024) return `${bytes}B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
}

private toggleVisibility(): void {
this.isVisible = !this.isVisible;
const dashboard = document.getElementById('performance-dashboard');
if (dashboard) {
dashboard.style.display = this.isVisible ? 'block' : 'none';
}
}

public exportPerformanceData(): string {
const report = this.profiler.generatePerformanceReport();
return JSON.stringify(report, null, 2);
}
}

📝 本章小结

通过本教程,你应该掌握了�?

  1. 性能分析基础: 理解GPU性能指标和分析方�?2. *着色器复杂度分�?: 学会评估着色器的计算复杂度
  2. 实时监控: 掌握实时性能监控和可视化技�?4. 瓶颈识别: 了解如何识别和定位性能瓶颈

🚀 下一步学�?

继续学习开发工具和工作流优化!🛠️✨