text-shaper 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,517 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Text Shaper Performance Benchmarks</title>
7
+ <style>
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ max-width: 1200px;
11
+ margin: 0 auto;
12
+ padding: 20px;
13
+ background: #f5f5f5;
14
+ }
15
+ .container {
16
+ background: white;
17
+ border-radius: 8px;
18
+ padding: 30px;
19
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
20
+ }
21
+ h1 {
22
+ color: #333;
23
+ text-align: center;
24
+ margin-bottom: 30px;
25
+ }
26
+ .controls {
27
+ display: flex;
28
+ gap: 15px;
29
+ margin-bottom: 30px;
30
+ flex-wrap: wrap;
31
+ }
32
+ button {
33
+ padding: 10px 20px;
34
+ border: none;
35
+ border-radius: 5px;
36
+ background: #007acc;
37
+ color: white;
38
+ cursor: pointer;
39
+ font-size: 14px;
40
+ transition: background 0.2s;
41
+ }
42
+ button:hover {
43
+ background: #005a9e;
44
+ }
45
+ button:disabled {
46
+ background: #ccc;
47
+ cursor: not-allowed;
48
+ }
49
+ .status {
50
+ padding: 15px;
51
+ border-radius: 5px;
52
+ margin-bottom: 20px;
53
+ font-weight: 500;
54
+ }
55
+ .status.running {
56
+ background: #fff3cd;
57
+ border: 1px solid #ffeaa7;
58
+ color: #856404;
59
+ }
60
+ .status.complete {
61
+ background: #d4edda;
62
+ border: 1px solid #c3e6cb;
63
+ color: #155724;
64
+ }
65
+ .status.error {
66
+ background: #f8d7da;
67
+ border: 1px solid #f5c6cb;
68
+ color: #721c24;
69
+ }
70
+ .gpu-info {
71
+ background: #e3f2fd;
72
+ border: 1px solid #bbdefb;
73
+ color: #1565c0;
74
+ padding: 15px;
75
+ border-radius: 5px;
76
+ margin-bottom: 20px;
77
+ }
78
+ .results {
79
+ margin-top: 30px;
80
+ }
81
+ .result-section {
82
+ margin-bottom: 25px;
83
+ padding: 20px;
84
+ background: #f8f9fa;
85
+ border-radius: 5px;
86
+ border-left: 4px solid #007acc;
87
+ }
88
+ .result-title {
89
+ font-weight: 600;
90
+ color: #333;
91
+ margin-bottom: 10px;
92
+ font-size: 16px;
93
+ }
94
+ .result-grid {
95
+ display: grid;
96
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
97
+ gap: 15px;
98
+ margin-top: 10px;
99
+ }
100
+ .result-item {
101
+ background: white;
102
+ padding: 15px;
103
+ border-radius: 5px;
104
+ border: 1px solid #e0e0e0;
105
+ }
106
+ .result-label {
107
+ font-size: 12px;
108
+ color: #666;
109
+ margin-bottom: 5px;
110
+ }
111
+ .result-value {
112
+ font-size: 18px;
113
+ font-weight: 600;
114
+ color: #333;
115
+ }
116
+ .winner {
117
+ color: #28a745;
118
+ }
119
+ .loser {
120
+ color: #dc3545;
121
+ }
122
+ .report {
123
+ margin-top: 30px;
124
+ padding: 20px;
125
+ background: #f8f9fa;
126
+ border-radius: 5px;
127
+ font-family: 'Courier New', monospace;
128
+ font-size: 12px;
129
+ white-space: pre-wrap;
130
+ overflow-x: auto;
131
+ }
132
+ .progress {
133
+ margin: 20px 0;
134
+ height: 4px;
135
+ background: #e0e0e0;
136
+ border-radius: 2px;
137
+ overflow: hidden;
138
+ }
139
+ .progress-bar {
140
+ height: 100%;
141
+ background: #007acc;
142
+ transition: width 0.3s ease;
143
+ }
144
+ canvas {
145
+ display: none;
146
+ }
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <div class="container">
151
+ <h1>🚀 Text Shaper Performance Benchmarks</h1>
152
+
153
+ <div id="gpu-info" class="gpu-info" style="display: none;">
154
+ <strong>GPU Information:</strong>
155
+ <div id="gpu-details"></div>
156
+ </div>
157
+
158
+ <div class="controls">
159
+ <button id="run-all" onclick="runAllBenchmarks()">Run All Benchmarks</button>
160
+ <button id="run-cpu" onclick="runCPUBenchmarks()">CPU Only</button>
161
+ <button id="run-gpu" onclick="runGPUBenchmarks()">GPU Only</button>
162
+ <button id="run-comparison" onclick="runComparison()">CPU vs GPU Comparison</button>
163
+ <button id="clear" onclick="clearResults()">Clear Results</button>
164
+ </div>
165
+
166
+ <div id="status" class="status" style="display: none;"></div>
167
+ <div id="progress" class="progress" style="display: none;">
168
+ <div id="progress-bar" class="progress-bar" style="width: 0%;"></div>
169
+ </div>
170
+
171
+ <div id="results" class="results"></div>
172
+ </div>
173
+
174
+ <canvas id="test-canvas"></canvas>
175
+
176
+ <script type="module">
177
+ import { getGPUInfo, createPerformanceReport } from '../utils/perf-utils.js';
178
+ import { runAllCPUBenchmarks } from '../cpu/cpu-perf.js';
179
+ import {
180
+ benchmarkWebGPUSDF,
181
+ benchmarkWebGPUAtlasPacking,
182
+ benchmarkWebGPUGlyphRendering,
183
+ initWebGPU
184
+ } from '../gpu/webgpu-perf.js';
185
+ import {
186
+ benchmarkWebGLAtlasRendering,
187
+ benchmarkWebGLSDF,
188
+ benchmarkWebGLGlyphBlitting,
189
+ initWebGL
190
+ } from '../gpu/webgl-perf.js';
191
+ import { runComprehensiveComparison, createComparisonReport } from '../comparison-tests.js';
192
+
193
+ let isRunning = false;
194
+
195
+ // Display GPU information
196
+ async function displayGPUInfo() {
197
+ try {
198
+ const info = await getGPUInfo();
199
+ const gpuInfo = document.getElementById('gpu-info');
200
+ const gpuDetails = document.getElementById('gpu-details');
201
+
202
+ gpuDetails.innerHTML = `
203
+ Vendor: ${info.vendor}<br>
204
+ Renderer: ${info.renderer}<br>
205
+ WebGPU: ${info.webgpu ? '✅ Supported' : '❌ Not supported'}<br>
206
+ WebGL: ${info.webgl ? '✅ Supported' : '❌ Not supported'}
207
+ `;
208
+
209
+ gpuInfo.style.display = 'block';
210
+ } catch (error) {
211
+ console.warn('Failed to get GPU info:', error);
212
+ }
213
+ }
214
+
215
+ // Update status
216
+ function updateStatus(message, type = 'running') {
217
+ const status = document.getElementById('status');
218
+ status.textContent = message;
219
+ status.className = `status ${type}`;
220
+ status.style.display = 'block';
221
+ }
222
+
223
+ // Update progress
224
+ function updateProgress(percent) {
225
+ const progress = document.getElementById('progress');
226
+ const progressBar = document.getElementById('progress-bar');
227
+
228
+ progress.style.display = 'block';
229
+ progressBar.style.width = `${percent}%`;
230
+ }
231
+
232
+ // Clear progress
233
+ function clearProgress() {
234
+ document.getElementById('progress').style.display = 'none';
235
+ document.getElementById('progress-bar').style.width = '0%';
236
+ }
237
+
238
+ // Display results
239
+ function displayResults(title, results, type = 'single') {
240
+ const resultsDiv = document.getElementById('results');
241
+
242
+ const section = document.createElement('div');
243
+ section.className = 'result-section';
244
+
245
+ const titleEl = document.createElement('div');
246
+ titleEl.className = 'result-title';
247
+ titleEl.textContent = title;
248
+ section.appendChild(titleEl);
249
+
250
+ if (type === 'comparison') {
251
+ displayComparisonResults(section, results);
252
+ } else {
253
+ displaySingleResults(section, results);
254
+ }
255
+
256
+ resultsDiv.appendChild(section);
257
+ }
258
+
259
+ function displaySingleResults(container, results) {
260
+ const grid = document.createElement('div');
261
+ grid.className = 'result-grid';
262
+
263
+ results.forEach(result => {
264
+ const item = document.createElement('div');
265
+ item.className = 'result-item';
266
+ item.innerHTML = `
267
+ <div class="result-label">${result.name}</div>
268
+ <div class="result-value">${result.avgTime.toFixed(3)} ms</div>
269
+ <div style="font-size: 11px; color: #666; margin-top: 5px;">
270
+ ${result.iterations} iterations<br>
271
+ Min: ${result.minTime.toFixed(3)} ms<br>
272
+ Max: ${result.maxTime.toFixed(3)} ms
273
+ </div>
274
+ `;
275
+ grid.appendChild(item);
276
+ });
277
+
278
+ container.appendChild(grid);
279
+ }
280
+
281
+ function displayComparisonResults(container, comparison) {
282
+ const tests = ['sdf', 'atlas', 'rendering'];
283
+ const testNames = {
284
+ sdf: 'SDF Generation',
285
+ atlas: 'Atlas Generation',
286
+ rendering: 'Glyph Rendering'
287
+ };
288
+
289
+ tests.forEach(test => {
290
+ const result = comparison[test];
291
+ const item = document.createElement('div');
292
+ item.className = 'result-item';
293
+ item.style.marginBottom = '15px';
294
+
295
+ let content = `<strong>${testNames[test]}</strong><br>`;
296
+ content += `CPU: ${result.cpu.avgTime.toFixed(3)} ms`;
297
+
298
+ if (result.webgpu) {
299
+ const gpuClass = result.winner === 'webgpu' ? 'winner' : 'loser';
300
+ content += `<br><span class="${gpuClass}">WebGPU: ${result.webgpu.avgTime.toFixed(3)} ms`;
301
+ if (result.speedupWebGPU) {
302
+ content += ` (${result.speedupWebGPU.toFixed(2)}x)`;
303
+ }
304
+ content += '</span>';
305
+ }
306
+
307
+ if (result.webgl) {
308
+ const gpuClass = result.winner === 'webgl' ? 'winner' : 'loser';
309
+ content += `<br><span class="${gpuClass}">WebGL: ${result.webgl.avgTime.toFixed(3)} ms`;
310
+ if (result.speedupWebGL) {
311
+ content += ` (${result.speedupWebGL.toFixed(2)}x)`;
312
+ }
313
+ content += '</span>';
314
+ }
315
+
316
+ content += `<br><strong>Winner: ${result.winner.toUpperCase()}</strong>`;
317
+
318
+ item.innerHTML = content;
319
+ container.appendChild(item);
320
+ });
321
+
322
+ // Summary
323
+ const summary = document.createElement('div');
324
+ summary.style.marginTop = '20px';
325
+ summary.innerHTML = `
326
+ <strong>Summary:</strong><br>
327
+ GPU Wins: ${comparison.summary.gpuWins}/${comparison.summary.totalTests}<br>
328
+ Average Speedup: ${comparison.summary.averageSpeedup.toFixed(2)}x<br>
329
+ Best GPU Technology: ${comparison.summary.bestGPU.toUpperCase()}
330
+ `;
331
+ container.appendChild(summary);
332
+ }
333
+
334
+ // Benchmark runners
335
+ window.runAllBenchmarks = async function() {
336
+ if (isRunning) return;
337
+ isRunning = true;
338
+
339
+ try {
340
+ updateStatus('Running all benchmarks...', 'running');
341
+ updateProgress(0);
342
+
343
+ const allResults = [];
344
+
345
+ // CPU benchmarks
346
+ updateStatus('Running CPU benchmarks...', 'running');
347
+ updateProgress(25);
348
+ const cpuResults = await runAllCPUBenchmarks({ iterations: 50 });
349
+ allResults.push(...cpuResults);
350
+ displayResults('CPU Benchmarks', cpuResults);
351
+
352
+ // GPU benchmarks
353
+ updateStatus('Running GPU benchmarks...', 'running');
354
+ updateProgress(50);
355
+
356
+ const gpuResults = [];
357
+
358
+ // WebGPU tests
359
+ try {
360
+ const webgpuContext = await initWebGPU();
361
+ if (webgpuContext) {
362
+ gpuResults.push(await benchmarkWebGPUSDF(webgpuContext, { iterations: 50 }));
363
+ gpuResults.push(await benchmarkWebGPUAtlasPacking(webgpuContext, { iterations: 50 }));
364
+ gpuResults.push(await benchmarkWebGPUGlyphRendering(webgpuContext, { iterations: 50 }));
365
+ }
366
+ } catch (error) {
367
+ console.warn('WebGPU benchmarks failed:', error);
368
+ }
369
+
370
+ // WebGL tests
371
+ try {
372
+ const webglContext = initWebGL();
373
+ if (webglContext) {
374
+ gpuResults.push(await benchmarkWebGLAtlasRendering(webglContext, { iterations: 50 }));
375
+ gpuResults.push(await benchmarkWebGLSDF(webglContext, { iterations: 50 }));
376
+ gpuResults.push(await benchmarkWebGLGlyphBlitting(webglContext, { iterations: 50 }));
377
+ }
378
+ } catch (error) {
379
+ console.warn('WebGL benchmarks failed:', error);
380
+ }
381
+
382
+ if (gpuResults.length > 0) {
383
+ displayResults('GPU Benchmarks', gpuResults);
384
+ }
385
+
386
+ updateProgress(100);
387
+ updateStatus(`Completed ${allResults.length + gpuResults.length} benchmarks`, 'complete');
388
+
389
+ } catch (error) {
390
+ updateStatus(`Error: ${error.message}`, 'error');
391
+ console.error('Benchmark error:', error);
392
+ } finally {
393
+ isRunning = false;
394
+ clearProgress();
395
+ }
396
+ };
397
+
398
+ window.runCPUBenchmarks = async function() {
399
+ if (isRunning) return;
400
+ isRunning = true;
401
+
402
+ try {
403
+ updateStatus('Running CPU benchmarks...', 'running');
404
+ updateProgress(50);
405
+
406
+ const results = await runAllCPUBenchmarks({ iterations: 50 });
407
+ displayResults('CPU Benchmarks', results);
408
+
409
+ updateProgress(100);
410
+ updateStatus('CPU benchmarks completed', 'complete');
411
+
412
+ } catch (error) {
413
+ updateStatus(`Error: ${error.message}`, 'error');
414
+ console.error('CPU benchmark error:', error);
415
+ } finally {
416
+ isRunning = false;
417
+ clearProgress();
418
+ }
419
+ };
420
+
421
+ window.runGPUBenchmarks = async function() {
422
+ if (isRunning) return;
423
+ isRunning = true;
424
+
425
+ try {
426
+ updateStatus('Running GPU benchmarks...', 'running');
427
+ updateProgress(0);
428
+
429
+ const results = [];
430
+
431
+ // WebGPU tests
432
+ updateStatus('Running WebGPU benchmarks...', 'running');
433
+ updateProgress(33);
434
+
435
+ try {
436
+ const webgpuContext = await initWebGPU();
437
+ if (webgpuContext) {
438
+ results.push(await benchmarkWebGPUSDF(webgpuContext, { iterations: 50 }));
439
+ results.push(await benchmarkWebGPUAtlasPacking(webgpuContext, { iterations: 50 }));
440
+ results.push(await benchmarkWebGPUGlyphRendering(webgpuContext, { iterations: 50 }));
441
+ }
442
+ } catch (error) {
443
+ console.warn('WebGPU benchmarks failed:', error);
444
+ }
445
+
446
+ // WebGL tests
447
+ updateStatus('Running WebGL benchmarks...', 'running');
448
+ updateProgress(66);
449
+
450
+ try {
451
+ const webglContext = initWebGL();
452
+ if (webglContext) {
453
+ results.push(await benchmarkWebGLAtlasRendering(webglContext, { iterations: 50 }));
454
+ results.push(await benchmarkWebGLSDF(webglContext, { iterations: 50 }));
455
+ results.push(await benchmarkWebGLGlyphBlitting(webglContext, { iterations: 50 }));
456
+ }
457
+ } catch (error) {
458
+ console.warn('WebGL benchmarks failed:', error);
459
+ }
460
+
461
+ if (results.length > 0) {
462
+ displayResults('GPU Benchmarks', results);
463
+ }
464
+
465
+ updateProgress(100);
466
+ updateStatus('GPU benchmarks completed', 'complete');
467
+
468
+ } catch (error) {
469
+ updateStatus(`Error: ${error.message}`, 'error');
470
+ console.error('GPU benchmark error:', error);
471
+ } finally {
472
+ isRunning = false;
473
+ clearProgress();
474
+ }
475
+ };
476
+
477
+ window.runComparison = async function() {
478
+ if (isRunning) return;
479
+ isRunning = true;
480
+
481
+ try {
482
+ updateStatus('Running CPU vs GPU comparison...', 'running');
483
+ updateProgress(50);
484
+
485
+ const comparison = await runComprehensiveComparison({ iterations: 30 });
486
+ displayResults('CPU vs GPU Performance Comparison', comparison, 'comparison');
487
+
488
+ // Display detailed report
489
+ const report = createComparisonReport(comparison);
490
+ const reportDiv = document.createElement('div');
491
+ reportDiv.className = 'report';
492
+ reportDiv.textContent = report;
493
+ document.getElementById('results').appendChild(reportDiv);
494
+
495
+ updateProgress(100);
496
+ updateStatus('Comparison completed', 'complete');
497
+
498
+ } catch (error) {
499
+ updateStatus(`Error: ${error.message}`, 'error');
500
+ console.error('Comparison error:', error);
501
+ } finally {
502
+ isRunning = false;
503
+ clearProgress();
504
+ }
505
+ };
506
+
507
+ window.clearResults = function() {
508
+ document.getElementById('results').innerHTML = '';
509
+ document.getElementById('status').style.display = 'none';
510
+ clearProgress();
511
+ };
512
+
513
+ // Initialize
514
+ displayGPUInfo();
515
+ </script>
516
+ </body>
517
+ </html>