foss22 commited on
Commit
e7169ff
·
verified ·
1 Parent(s): ba7f9c6

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1202 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Benchmark Mobile Gpu
3
- emoji:
4
- colorFrom: red
5
- colorTo: pink
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: benchmark-mobile-gpu
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1202 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>GPU Benchmark for Mobile</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ :root {
10
+ --primary: #6200ee;
11
+ --primary-dark: #3700b3;
12
+ --secondary: #03dac6;
13
+ --background: #121212;
14
+ --surface: #1e1e1e;
15
+ --error: #cf6679;
16
+ --text-primary: rgba(255, 255, 255, 0.87);
17
+ --text-secondary: rgba(255, 255, 255, 0.6);
18
+ }
19
+
20
+ * {
21
+ margin: 0;
22
+ padding: 0;
23
+ box-sizing: border-box;
24
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
25
+ }
26
+
27
+ body {
28
+ background-color: var(--background);
29
+ color: var(--text-primary);
30
+ min-height: 100vh;
31
+ display: flex;
32
+ flex-direction: column;
33
+ padding: 20px;
34
+ }
35
+
36
+ header {
37
+ text-align: center;
38
+ margin-bottom: 30px;
39
+ padding: 20px;
40
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
41
+ }
42
+
43
+ h1 {
44
+ font-size: 2rem;
45
+ margin-bottom: 10px;
46
+ background: linear-gradient(45deg, var(--primary), var(--secondary));
47
+ -webkit-background-clip: text;
48
+ background-clip: text;
49
+ color: transparent;
50
+ }
51
+
52
+ .subtitle {
53
+ color: var(--text-secondary);
54
+ font-size: 0.9rem;
55
+ }
56
+
57
+ .card {
58
+ background-color: var(--surface);
59
+ border-radius: 12px;
60
+ padding: 20px;
61
+ margin-bottom: 20px;
62
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
63
+ }
64
+
65
+ .test-container {
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: 15px;
69
+ margin-top: 20px;
70
+ }
71
+
72
+ .test-item {
73
+ display: flex;
74
+ justify-content: space-between;
75
+ align-items: center;
76
+ padding: 12px 15px;
77
+ background-color: rgba(255, 255, 255, 0.05);
78
+ border-radius: 8px;
79
+ transition: all 0.3s ease;
80
+ }
81
+
82
+ .test-item:hover {
83
+ background-color: rgba(255, 255, 255, 0.1);
84
+ }
85
+
86
+ .test-info {
87
+ display: flex;
88
+ flex-direction: column;
89
+ }
90
+
91
+ .test-name {
92
+ font-weight: 500;
93
+ }
94
+
95
+ .test-description {
96
+ font-size: 0.8rem;
97
+ color: var(--text-secondary);
98
+ }
99
+
100
+ .test-result {
101
+ font-weight: 600;
102
+ }
103
+
104
+ .progress-container {
105
+ width: 100%;
106
+ height: 6px;
107
+ background-color: rgba(255, 255, 255, 0.1);
108
+ border-radius: 3px;
109
+ margin-top: 5px;
110
+ overflow: hidden;
111
+ }
112
+
113
+ .progress-bar {
114
+ height: 100%;
115
+ background: linear-gradient(90deg, var(--primary), var(--secondary));
116
+ border-radius: 3px;
117
+ width: 0%;
118
+ transition: width 0.5s ease;
119
+ }
120
+
121
+ button {
122
+ background-color: var(--primary);
123
+ color: white;
124
+ border: none;
125
+ padding: 12px 24px;
126
+ border-radius: 25px;
127
+ font-size: 1rem;
128
+ font-weight: 500;
129
+ cursor: pointer;
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ gap: 10px;
134
+ width: 100%;
135
+ transition: all 0.3s ease;
136
+ box-shadow: 0 2px 10px rgba(98, 0, 238, 0.3);
137
+ }
138
+
139
+ button:hover {
140
+ background-color: var(--primary-dark);
141
+ transform: translateY(-2px);
142
+ box-shadow: 0 4px 15px rgba(98, 0, 238, 0.4);
143
+ }
144
+
145
+ button:disabled {
146
+ background-color: rgba(98, 0, 238, 0.5);
147
+ cursor: not-allowed;
148
+ transform: none;
149
+ box-shadow: none;
150
+ }
151
+
152
+ .result-container {
153
+ margin-top: 30px;
154
+ opacity: 0;
155
+ transition: opacity 0.5s ease;
156
+ }
157
+
158
+ .result-container.show {
159
+ opacity: 1;
160
+ }
161
+
162
+ .score {
163
+ font-size: 3rem;
164
+ font-weight: 700;
165
+ text-align: center;
166
+ margin: 20px 0;
167
+ background: linear-gradient(45deg, var(--secondary), var(--primary));
168
+ -webkit-background-clip: text;
169
+ background-clip: text;
170
+ color: transparent;
171
+ }
172
+
173
+ .device-info {
174
+ display: grid;
175
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
176
+ gap: 10px;
177
+ margin-top: 20px;
178
+ }
179
+
180
+ .info-item {
181
+ background-color: rgba(255, 255, 255, 0.05);
182
+ padding: 10px;
183
+ border-radius: 8px;
184
+ display: flex;
185
+ flex-direction: column;
186
+ }
187
+
188
+ .info-label {
189
+ font-size: 0.7rem;
190
+ color: var(--text-secondary);
191
+ margin-bottom: 5px;
192
+ }
193
+
194
+ .info-value {
195
+ font-size: 0.9rem;
196
+ font-weight: 500;
197
+ }
198
+
199
+ .canvas-container {
200
+ position: fixed;
201
+ top: 0;
202
+ left: 0;
203
+ width: 100%;
204
+ height: 100%;
205
+ pointer-events: none;
206
+ z-index: -1;
207
+ opacity: 0.1;
208
+ }
209
+
210
+ canvas {
211
+ display: block;
212
+ }
213
+
214
+ .spinner {
215
+ width: 20px;
216
+ height: 20px;
217
+ border: 3px solid rgba(255, 255, 255, 0.3);
218
+ border-radius: 50%;
219
+ border-top-color: white;
220
+ animation: spin 1s ease-in-out infinite;
221
+ display: none;
222
+ }
223
+
224
+ @keyframes spin {
225
+ to { transform: rotate(360deg); }
226
+ }
227
+
228
+ footer {
229
+ margin-top: auto;
230
+ text-align: center;
231
+ padding: 20px;
232
+ color: var(--text-secondary);
233
+ font-size: 0.8rem;
234
+ }
235
+
236
+ .share-buttons {
237
+ display: flex;
238
+ gap: 10px;
239
+ margin-top: 20px;
240
+ }
241
+
242
+ .share-button {
243
+ flex: 1;
244
+ background-color: rgba(255, 255, 255, 0.1);
245
+ color: white;
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ gap: 8px;
250
+ }
251
+
252
+ .share-button:hover {
253
+ background-color: rgba(255, 255, 255, 0.2);
254
+ }
255
+
256
+ @media (max-width: 480px) {
257
+ h1 {
258
+ font-size: 1.5rem;
259
+ }
260
+
261
+ .device-info {
262
+ grid-template-columns: 1fr 1fr;
263
+ }
264
+ }
265
+ </style>
266
+ </head>
267
+ <body>
268
+ <div class="canvas-container">
269
+ <canvas id="backgroundCanvas"></canvas>
270
+ </div>
271
+
272
+ <header>
273
+ <h1>Mobile GPU Benchmark</h1>
274
+ <p class="subtitle">Test your device's WebGL performance in the browser</p>
275
+ </header>
276
+
277
+ <main>
278
+ <div class="card">
279
+ <div class="test-container">
280
+ <div class="test-item">
281
+ <div class="test-info">
282
+ <span class="test-name">WebGL Support</span>
283
+ <span class="test-description">Checking WebGL availability</span>
284
+ <div class="progress-container">
285
+ <div class="progress-bar" id="webgl-progress"></div>
286
+ </div>
287
+ </div>
288
+ <span class="test-result" id="webgl-result">-</span>
289
+ </div>
290
+
291
+ <div class="test-item">
292
+ <div class="test-info">
293
+ <span class="test-name">Shader Complexity</span>
294
+ <span class="test-description">Testing fragment shader performance</span>
295
+ <div class="progress-container">
296
+ <div class="progress-bar" id="shader-progress"></div>
297
+ </div>
298
+ </div>
299
+ <span class="test-result" id="shader-result">-</span>
300
+ </div>
301
+
302
+ <div class="test-item">
303
+ <div class="test-info">
304
+ <span class="test-name">Texture Handling</span>
305
+ <span class="test-description">Testing texture rendering capabilities</span>
306
+ <div class="progress-container">
307
+ <div class="progress-bar" id="texture-progress"></div>
308
+ </div>
309
+ </div>
310
+ <span class="test-result" id="texture-result">-</span>
311
+ </div>
312
+
313
+ <div class="test-item">
314
+ <div class="test-info">
315
+ <span class="test-name">Geometry Processing</span>
316
+ <span class="test-description">Testing vertex processing speed</span>
317
+ <div class="progress-container">
318
+ <div class="progress-bar" id="geometry-progress"></div>
319
+ </div>
320
+ </div>
321
+ <span class="test-result" id="geometry-result">-</span>
322
+ </div>
323
+
324
+ <div class="test-item">
325
+ <div class="test-info">
326
+ <span class="test-name">Compute Performance</span>
327
+ <span class="test-description">Testing general computational power</span>
328
+ <div class="progress-container">
329
+ <div class="progress-bar" id="compute-progress"></div>
330
+ </div>
331
+ </div>
332
+ <span class="test-result" id="compute-result">-</span>
333
+ </div>
334
+ </div>
335
+
336
+ <button id="runBenchmark">
337
+ <span id="buttonText">Run Benchmark</span>
338
+ <div class="spinner" id="spinner"></div>
339
+ </button>
340
+ </div>
341
+
342
+ <div class="card result-container" id="resultContainer">
343
+ <h2>Benchmark Results</h2>
344
+ <div class="score" id="finalScore">0</div>
345
+
346
+ <div class="device-info">
347
+ <div class="info-item">
348
+ <span class="info-label">Device Performance</span>
349
+ <span class="info-value" id="performance-category">-</span>
350
+ </div>
351
+ <div class="info-item">
352
+ <span class="info-label">Renderer</span>
353
+ <span class="info-value" id="renderer-info">-</span>
354
+ </div>
355
+ <div class="info-item">
356
+ <span class="info-label">Resolution</span>
357
+ <span class="info-value" id="resolution-info">-</span>
358
+ </div>
359
+ <div class="info-item">
360
+ <span class="info-label">FPS</span>
361
+ <span class="info-value" id="fps-info">-</span>
362
+ </div>
363
+ </div>
364
+
365
+ <div class="share-buttons">
366
+ <button class="share-button" id="shareButton">
367
+ <i class="fas fa-share-alt"></i>
368
+ <span>Share</span>
369
+ </button>
370
+ <button class="share-button" id="saveButton">
371
+ <i class="fas fa-save"></i>
372
+ <span>Save</span>
373
+ </button>
374
+ </div>
375
+ </div>
376
+ </main>
377
+
378
+ <footer>
379
+ <p>This benchmark tests your device's WebGL 1.0 capabilities. Results may vary between browsers.</p>
380
+ </footer>
381
+
382
+ <script>
383
+ document.addEventListener('DOMContentLoaded', function() {
384
+ // Elements
385
+ const runButton = document.getElementById('runBenchmark');
386
+ const buttonText = document.getElementById('buttonText');
387
+ const spinner = document.getElementById('spinner');
388
+ const resultContainer = document.getElementById('resultContainer');
389
+ const finalScore = document.getElementById('finalScore');
390
+
391
+ // Test result elements
392
+ const testResults = {
393
+ webgl: document.getElementById('webgl-result'),
394
+ shader: document.getElementById('shader-result'),
395
+ texture: document.getElementById('texture-result'),
396
+ geometry: document.getElementById('geometry-result'),
397
+ compute: document.getElementById('compute-result')
398
+ };
399
+
400
+ // Progress bars
401
+ const progressBars = {
402
+ webgl: document.getElementById('webgl-progress'),
403
+ shader: document.getElementById('shader-progress'),
404
+ texture: document.getElementById('texture-progress'),
405
+ geometry: document.getElementById('geometry-progress'),
406
+ compute: document.getElementById('compute-progress')
407
+ };
408
+
409
+ // Device info
410
+ const performanceCategory = document.getElementById('performance-category');
411
+ const rendererInfo = document.getElementById('renderer-info');
412
+ const resolutionInfo = document.getElementById('resolution-info');
413
+ const fpsInfo = document.getElementById('fps-info');
414
+
415
+ // Background animation
416
+ const backgroundCanvas = document.getElementById('backgroundCanvas');
417
+ let gl;
418
+
419
+ // Set canvas size
420
+ function resizeCanvas() {
421
+ backgroundCanvas.width = window.innerWidth;
422
+ backgroundCanvas.height = window.innerHeight;
423
+
424
+ if (gl) {
425
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
426
+ }
427
+ }
428
+
429
+ // Initialize WebGL
430
+ function initWebGL() {
431
+ try {
432
+ gl = backgroundCanvas.getContext('webgl') || backgroundCanvas.getContext('experimental-webgl');
433
+
434
+ if (!gl) {
435
+ return false;
436
+ }
437
+
438
+ // Simple vertex shader
439
+ const vsSource = `
440
+ attribute vec2 aPosition;
441
+ void main() {
442
+ gl_Position = vec4(aPosition, 0.0, 1.0);
443
+ }
444
+ `;
445
+
446
+ // Fragment shader for background animation
447
+ const fsSource = `
448
+ precision highp float;
449
+
450
+ uniform float uTime;
451
+ uniform vec2 uResolution;
452
+
453
+ void main() {
454
+ vec2 uv = gl_FragCoord.xy / uResolution.xy;
455
+ uv.x *= uResolution.x / uResolution.y;
456
+
457
+ float time = uTime * 0.3;
458
+
459
+ // Animated gradient with noise
460
+ vec3 col = vec3(
461
+ sin(uv.x * 2.0 + time) * 0.5 + 0.5,
462
+ cos(uv.y * 3.0 - time * 1.5) * 0.5 + 0.5,
463
+ sin((uv.x + uv.y) * 5.0 + time * 2.0) * 0.5 + 0.5
464
+ );
465
+
466
+ gl_FragColor = vec4(col * 0.3, 1.0);
467
+ }
468
+ `;
469
+
470
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
471
+ gl.shaderSource(vertexShader, vsSource);
472
+ gl.compileShader(vertexShader);
473
+
474
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
475
+ gl.shaderSource(fragmentShader, fsSource);
476
+ gl.compileShader(fragmentShader);
477
+
478
+ const shaderProgram = gl.createProgram();
479
+ gl.attachShader(shaderProgram, vertexShader);
480
+ gl.attachShader(shaderProgram, fragmentShader);
481
+ gl.linkProgram(shaderProgram);
482
+ gl.useProgram(shaderProgram);
483
+
484
+ // Create position buffer
485
+ const positions = [-1, -1, 1, -1, -1, 1, 1, 1];
486
+ const positionBuffer = gl.createBuffer();
487
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
488
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
489
+
490
+ // Bind position attribute
491
+ const aPosition = gl.getAttribLocation(shaderProgram, 'aPosition');
492
+ gl.enableVertexAttribArray(aPosition);
493
+ gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
494
+
495
+ // Get uniform locations
496
+ const uTime = gl.getUniformLocation(shaderProgram, 'uTime');
497
+ const uResolution = gl.getUniformLocation(shaderProgram, 'uResolution');
498
+
499
+ // Animation loop
500
+ let startTime = Date.now();
501
+
502
+ function animate() {
503
+ const time = (Date.now() - startTime) / 1000;
504
+
505
+ gl.uniform1f(uTime, time);
506
+ gl.uniform2f(uResolution, gl.drawingBufferWidth, gl.drawingBufferHeight);
507
+
508
+ gl.clear(gl.COLOR_BUFFER_BIT);
509
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
510
+
511
+ requestAnimationFrame(animate);
512
+ }
513
+
514
+ animate();
515
+
516
+ return true;
517
+ } catch (e) {
518
+ console.error('WebGL initialization failed:', e);
519
+ return false;
520
+ }
521
+ }
522
+
523
+ // Initialize on load
524
+ window.addEventListener('load', function() {
525
+ resizeCanvas();
526
+
527
+ // Initial WebGL test
528
+ const webglSupported = initWebGL();
529
+ testResults.webgl.textContent = webglSupported ? 'Supported' : 'Not Supported';
530
+ progressBars.webgl.style.width = webglSupported ? '100%' : '0%';
531
+
532
+ if (!webglSupported) {
533
+ runButton.disabled = true;
534
+ buttonText.textContent = 'WebGL Not Supported';
535
+ }
536
+
537
+ // Set device info
538
+ if (webglSupported && gl) {
539
+ const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
540
+ if (debugInfo) {
541
+ const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
542
+ rendererInfo.textContent = renderer.split('/')[0]; // Clean up long strings
543
+ }
544
+ }
545
+
546
+ resolutionInfo.textContent = `${window.innerWidth} × ${window.innerHeight}`;
547
+ });
548
+
549
+ window.addEventListener('resize', function() {
550
+ resizeCanvas();
551
+ resolutionInfo.textContent = `${window.innerWidth} × ${window.innerHeight}`;
552
+ });
553
+
554
+ // Benchmark tests
555
+ async function runBenchmark() {
556
+ runButton.disabled = true;
557
+ buttonText.textContent = 'Running...';
558
+ spinner.style.display = 'block';
559
+
560
+ // Reset progress bars and results
561
+ for (const key in progressBars) {
562
+ progressBars[key].style.width = '0%';
563
+ testResults[key].textContent = '-';
564
+ }
565
+
566
+ // Wait for initial rendering
567
+ await new Promise(resolve => setTimeout(resolve, 300));
568
+
569
+ try {
570
+ let totalScore = 0;
571
+
572
+ if (!gl) {
573
+ throw new Error('WebGL not available');
574
+ }
575
+
576
+ // 1. WebGL Support (already checked)
577
+ animateProgress(progressBars.webgl, 100, 200);
578
+ testResults.webgl.textContent = 'Supported';
579
+ const webglScore = 100;
580
+ totalScore += webglScore;
581
+ await new Promise(resolve => setTimeout(resolve, 500));
582
+
583
+ // 2. Shader Complexity Test
584
+ const shaderScore = await runShaderTest();
585
+ animateProgress(progressBars.shader, shaderScore, 500);
586
+ testResults.shader.textContent = `${shaderScore} FPS`;
587
+ totalScore += shaderScore;
588
+ await new Promise(resolve => setTimeout(resolve, 500));
589
+
590
+ // 3. Texture Handling Test
591
+ const textureScore = await runTextureTest();
592
+ animateProgress(progressBars.texture, textureScore, 500);
593
+ testResults.texture.textContent = `${textureScore} MB/s`;
594
+ totalScore += textureScore;
595
+ await new Promise(resolve => setTimeout(resolve, 500));
596
+
597
+ // 4. Geometry Processing Test
598
+ const geometryScore = await runGeometryTest();
599
+ animateProgress(progressBars.geometry, geometryScore, 500);
600
+ testResults.geometry.textContent = `${geometryScore} K Tris`;
601
+ totalScore += geometryScore;
602
+ await new Promise(resolve => setTimeout(resolve, 500));
603
+
604
+ // 5. Compute Performance Test
605
+ const computeScore = await runComputeTest();
606
+ animateProgress(progressBars.compute, computeScore, 500);
607
+ testResults.compute.textContent = `${computeScore} Ops/s`;
608
+ totalScore += computeScore;
609
+ await new Promise(resolve => setTimeout(resolve, 500));
610
+
611
+ // Calculate final score (normalized)
612
+ const normalizedScore = Math.round(totalScore / 4);
613
+ finalScore.textContent = normalizedScore;
614
+ fpsInfo.textContent = `${shaderScore} FPS`;
615
+
616
+ // Set performance category
617
+ if (normalizedScore >= 80) {
618
+ performanceCategory.textContent = 'Excellent';
619
+ } else if (normalizedScore >= 60) {
620
+ performanceCategory.textContent = 'Good';
621
+ } else if (normalizedScore >= 40) {
622
+ performanceCategory.textContent = 'Average';
623
+ } else if (normalizedScore >= 20) {
624
+ performanceCategory.textContent = 'Below Average';
625
+ } else {
626
+ performanceCategory.textContent = 'Poor';
627
+ }
628
+
629
+ // Show results
630
+ resultContainer.classList.add('show');
631
+
632
+ } catch (error) {
633
+ console.error('Benchmark failed:', error);
634
+ alert('Benchmark failed: ' + error.message);
635
+ } finally {
636
+ runButton.disabled = false;
637
+ buttonText.textContent = 'Run Again';
638
+ spinner.style.display = 'none';
639
+ }
640
+ }
641
+
642
+ function animateProgress(progressBar, targetPercent, duration) {
643
+ const startTime = Date.now();
644
+ const interval = setInterval(() => {
645
+ const elapsed = Date.now() - startTime;
646
+ const progress = Math.min(elapsed / duration, 1);
647
+ const currentWidth = progress * targetPercent;
648
+
649
+ progressBar.style.width = `${currentWidth}%`;
650
+
651
+ if (progress === 1) {
652
+ clearInterval(interval);
653
+ }
654
+ }, 16);
655
+ }
656
+
657
+ async function runShaderTest() {
658
+ return new Promise(resolve => {
659
+ // Measure FPS with a complex shader
660
+ const testCanvas = document.createElement('canvas');
661
+ testCanvas.width = 512;
662
+ testCanvas.height = 512;
663
+
664
+ const testGl = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
665
+ if (!testGl) {
666
+ resolve(0);
667
+ return;
668
+ }
669
+
670
+ // Complex fragment shader for testing
671
+ const fsSource = `
672
+ precision highp float;
673
+
674
+ uniform float uTime;
675
+ uniform vec2 uResolution;
676
+
677
+ float noise(vec3 p) {
678
+ vec3 i = floor(p);
679
+ vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.);
680
+ vec3 f = cos((p-i)*acos(-1.))*(-.5)+.5;
681
+ a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x);
682
+ a.xy = mix(a.xz, a.yw, f.y);
683
+ return mix(a.x, a.y, f.z);
684
+ }
685
+
686
+ float sphere(vec3 p, vec4 spr) {
687
+ return length(spr.xyz-p) - spr.w;
688
+ }
689
+
690
+ float scene(vec3 p) {
691
+ float d = sphere(p, vec4(0.0, 0.0, 0.0, 1.0));
692
+ for (int i = 0; i < 5; i++) {
693
+ float fi = float(i);
694
+ d = min(d, sphere(p, vec4(
695
+ sin(uTime+fi*2.0)*2.0,
696
+ cos(uTime+fi*1.5)*2.0,
697
+ sin(uTime+fi*0.9)*2.0,
698
+ 0.7
699
+ )));
700
+ }
701
+ return d;
702
+ }
703
+
704
+ vec3 getNormal(vec3 p) {
705
+ vec3 eps = vec3(0.001, 0.0, 0.0);
706
+ return normalize(vec3(
707
+ scene(p+eps.xyy) - scene(p-eps.xyy),
708
+ scene(p+eps.yxy) - scene(p-eps.yxy),
709
+ scene(p+eps.yyx) - scene(p-eps.yyx)
710
+ ));
711
+ }
712
+
713
+ void main() {
714
+ vec2 uv = gl_FragCoord.xy / uResolution.xy;
715
+ uv = uv * 2.0 - 1.0;
716
+ uv.x *= uResolution.x / uResolution.y;
717
+
718
+ vec3 rd = normalize(vec3(uv, 1.0));
719
+ vec3 ro = vec3(0.0, 0.0, -3.0);
720
+ vec3 cp = vec3(0.0, 0.0, -2.0);
721
+
722
+ float t = 0.0;
723
+ for (int i = 0; i < 32; i++) {
724
+ vec3 p = ro + rd * t;
725
+ float d = scene(p);
726
+ t += d;
727
+ if (d < 0.001 || t > 20.0) break;
728
+ }
729
+
730
+ vec3 p = ro + rd * t;
731
+ vec3 n = getNormal(p);
732
+ vec3 l = normalize(vec3(1.0, 1.0, 1.0));
733
+ float diff = max(0.0, dot(n, l));
734
+ vec3 col = vec3(diff) + vec3(0.1);
735
+
736
+ gl_FragColor = vec4(col, 1.0);
737
+ }
738
+ `;
739
+
740
+ const vsSource = `
741
+ attribute vec2 aPosition;
742
+ void main() {
743
+ gl_Position = vec4(aPosition, 0.0, 1.0);
744
+ }
745
+ `;
746
+
747
+ const vertexShader = testGl.createShader(testGl.VERTEX_SHADER);
748
+ testGl.shaderSource(vertexShader, vsSource);
749
+ testGl.compileShader(vertexShader);
750
+
751
+ const fragmentShader = testGl.createShader(testGl.FRAGMENT_SHADER);
752
+ testGl.shaderSource(fragmentShader, fsSource);
753
+ testGl.compileShader(fragmentShader);
754
+
755
+ const shaderProgram = testGl.createProgram();
756
+ testGl.attachShader(shaderProgram, vertexShader);
757
+ testGl.attachShader(shaderProgram, fragmentShader);
758
+ testGl.linkProgram(shaderProgram);
759
+ testGl.useProgram(shaderProgram);
760
+
761
+ const positions = [-1, -1, 1, -1, -1, 1, 1, 1];
762
+ const positionBuffer = testGl.createBuffer();
763
+ testGl.bindBuffer(testGl.ARRAY_BUFFER, positionBuffer);
764
+ testGl.bufferData(testGl.ARRAY_BUFFER, new Float32Array(positions), testGl.STATIC_DRAW);
765
+
766
+ const aPosition = testGl.getAttribLocation(shaderProgram, 'aPosition');
767
+ testGl.enableVertexAttribArray(aPosition);
768
+ testGl.vertexAttribPointer(aPosition, 2, testGl.FLOAT, false, 0, 0);
769
+
770
+ const uTime = testGl.getUniformLocation(shaderProgram, 'uTime');
771
+ const uResolution = testGl.getUniformLocation(shaderProgram, 'uResolution');
772
+
773
+ // Measure FPS for 2 seconds
774
+ let frameCount = 0;
775
+ let fps = 0;
776
+ let startTime = performance.now();
777
+ let lastTime = startTime;
778
+
779
+ function render() {
780
+ const now = performance.now();
781
+ frameCount++;
782
+
783
+ if (now - startTime >= 2000) {
784
+ fps = Math.round(frameCount / ((now - startTime) / 1000));
785
+ resolve(fps);
786
+ return;
787
+ }
788
+
789
+ testGl.uniform1f(uTime, (now - startTime) / 1000);
790
+ testGl.uniform2f(uResolution, testCanvas.width, testCanvas.height);
791
+
792
+ testGl.clear(testGl.COLOR_BUFFER_BIT);
793
+ testGl.drawArrays(testGl.TRIANGLE_STRIP, 0, 4);
794
+
795
+ requestAnimationFrame(render);
796
+ }
797
+
798
+ render();
799
+ });
800
+ }
801
+
802
+ async function runTextureTest() {
803
+ return new Promise(resolve => {
804
+ const testCanvas = document.createElement('canvas');
805
+ testCanvas.width = 512;
806
+ testCanvas.height = 512;
807
+
808
+ const testGl = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
809
+ if (!testGl) {
810
+ resolve(0);
811
+ return;
812
+ }
813
+
814
+ // Create texture (512x512 RGBA)
815
+ const texture = testGl.createTexture();
816
+ testGl.bindTexture(testGl.TEXTURE_2D, texture);
817
+
818
+ const texSize = 512;
819
+ const texData = new Uint8Array(texSize * texSize * 4);
820
+ for (let i = 0; i < texSize * texSize * 4; i += 4) {
821
+ texData[i] = Math.random() * 255; // R
822
+ texData[i+1] = Math.random() * 255; // G
823
+ texData[i+2] = Math.random() * 255; // B
824
+ texData[i+3] = 255; // A
825
+ }
826
+
827
+ // Upload texture
828
+ testGl.texImage2D(testGl.TEXTURE_2D, 0, testGl.RGBA, texSize, texSize, 0, testGl.RGBA, testGl.UNSIGNED_BYTE, texData);
829
+ testGl.texParameteri(testGl.TEXTURE_2D, testGl.TEXTURE_MIN_FILTER, testGl.NEAREST);
830
+ testGl.texParameteri(testGl.TEXTURE_2D, testGl.TEXTURE_MAG_FILTER, testGl.NEAREST);
831
+
832
+ // Create simple shader
833
+ const vsSource = `
834
+ attribute vec2 aPosition;
835
+ attribute vec2 aTexCoord;
836
+ varying vec2 vTexCoord;
837
+ void main() {
838
+ gl_Position = vec4(aPosition, 0.0, 1.0);
839
+ vTexCoord = aTexCoord;
840
+ }
841
+ `;
842
+
843
+ const fsSource = `
844
+ precision highp float;
845
+ varying vec2 vTexCoord;
846
+ uniform sampler2D uTexture;
847
+ void main() {
848
+ gl_FragColor = texture2D(uTexture, vTexCoord);
849
+ }
850
+ `;
851
+
852
+ const vertexShader = testGl.createShader(testGl.VERTEX_SHADER);
853
+ testGl.shaderSource(vertexShader, vsSource);
854
+ testGl.compileShader(vertexShader);
855
+
856
+ const fragmentShader = testGl.createShader(testGl.FRAGMENT_SHADER);
857
+ testGl.shaderSource(fragmentShader, fsSource);
858
+ testGl.compileShader(fragmentShader);
859
+
860
+ const shaderProgram = testGl.createProgram();
861
+ testGl.attachShader(shaderProgram, vertexShader);
862
+ testGl.attachShader(shaderProgram, fragmentShader);
863
+ testGl.linkProgram(shaderProgram);
864
+ testGl.useProgram(shaderProgram);
865
+
866
+ // Fullscreen quad with tex coords
867
+ const positions = [-1, -1, 1, -1, -1, 1, 1, 1];
868
+ const texCoords = [0, 0, 1, 0, 0, 1, 1, 1];
869
+
870
+ const positionBuffer = testGl.createBuffer();
871
+ testGl.bindBuffer(testGl.ARRAY_BUFFER, positionBuffer);
872
+ testGl.bufferData(testGl.ARRAY_BUFFER, new Float32Array(positions), testGl.STATIC_DRAW);
873
+
874
+ const texCoordBuffer = testGl.createBuffer();
875
+ testGl.bindBuffer(testGl.ARRAY_BUFFER, texCoordBuffer);
876
+ testGl.bufferData(testGl.ARRAY_BUFFER, new Float32Array(texCoords), testGl.STATIC_DRAW);
877
+
878
+ const aPosition = testGl.getAttribLocation(shaderProgram, 'aPosition');
879
+ testGl.enableVertexAttribArray(aPosition);
880
+ testGl.bindBuffer(testGl.ARRAY_BUFFER, positionBuffer);
881
+ testGl.vertexAttribPointer(aPosition, 2, testGl.FLOAT, false, 0, 0);
882
+
883
+ const aTexCoord = testGl.getAttribLocation(shaderProgram, 'aTexCoord');
884
+ testGl.enableVertexAttribArray(aTexCoord);
885
+ testGl.bindBuffer(testGl.ARRAY_BUFFER, texCoordBuffer);
886
+ testGl.vertexAttribPointer(aTexCoord, 2, testGl.FLOAT, false, 0, 0);
887
+
888
+ const uTexture = testGl.getUniformLocation(shaderProgram, 'uTexture');
889
+ testGl.uniform1i(uTexture, 0);
890
+
891
+ // Measure texture rendering speed
892
+ let frameCount = 0;
893
+ let startTime = performance.now();
894
+ let mbPerSecond = 0;
895
+
896
+ function render() {
897
+ frameCount++;
898
+
899
+ if (frameCount === 100) {
900
+ const duration = (performance.now() - startTime) / 1000; // in seconds
901
+ const totalData = 512 * 512 * 4 * frameCount; // bytes
902
+ mbPerSecond = Math.round((totalData / (1024 * 1024)) / duration);
903
+ resolve(mbPerSecond);
904
+ return;
905
+ }
906
+
907
+ // Modify texture to force upload
908
+ if (frameCount % 10 === 0) {
909
+ for (let i = 0; i < texSize * texSize * 4; i += 4) {
910
+ texData[i] = Math.random() * 255;
911
+ texData[i+1] = Math.random() * 255;
912
+ texData[i+2] = Math.random() * 255;
913
+ }
914
+ testGl.texSubImage2D(testGl.TEXTURE_2D, 0, 0, 0, texSize, texSize, testGl.RGBA, testGl.UNSIGNED_BYTE, texData);
915
+ }
916
+
917
+ testGl.clear(testGl.COLOR_BUFFER_BIT);
918
+ testGl.drawArrays(testGl.TRIANGLE_STRIP, 0, 4);
919
+
920
+ requestAnimationFrame(render);
921
+ }
922
+
923
+ render();
924
+ });
925
+ }
926
+
927
+ async function runGeometryTest() {
928
+ return new Promise(resolve => {
929
+ const testCanvas = document.createElement('canvas');
930
+ testCanvas.width = 512;
931
+ testCanvas.height = 512;
932
+
933
+ const testGl = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
934
+ if (!testGl) {
935
+ resolve(0);
936
+ return;
937
+ }
938
+
939
+ // Create simple shader
940
+ const vsSource = `
941
+ attribute vec2 aPosition;
942
+ void main() {
943
+ gl_Position = vec4(aPosition, 0.0, 1.0);
944
+ gl_PointSize = 2.0;
945
+ }
946
+ `;
947
+
948
+ const fsSource = `
949
+ precision highp float;
950
+ void main() {
951
+ gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
952
+ }
953
+ `;
954
+
955
+ const vertexShader = testGl.createShader(testGl.VERTEX_SHADER);
956
+ testGl.shaderSource(vertexShader, vsSource);
957
+ testGl.compileShader(vertexShader);
958
+
959
+ const fragmentShader = testGl.createShader(testGl.FRAGMENT_SHADER);
960
+ testGl.shaderSource(fragmentShader, fsSource);
961
+ testGl.compileShader(fragmentShader);
962
+
963
+ const shaderProgram = testGl.createProgram();
964
+ testGl.attachShader(shaderProgram, vertexShader);
965
+ testGl.attachShader(shaderProgram, fragmentShader);
966
+ testGl.linkProgram(shaderProgram);
967
+ testGl.useProgram(shaderProgram);
968
+
969
+ // Create position buffer (will be updated each frame)
970
+ const positionBuffer = testGl.createBuffer();
971
+ testGl.bindBuffer(testGl.ARRAY_BUFFER, positionBuffer);
972
+
973
+ const aPosition = testGl.getAttribLocation(shaderProgram, 'aPosition');
974
+ testGl.enableVertexAttribArray(aPosition);
975
+ testGl.vertexAttribPointer(aPosition, 2, testGl.FLOAT, false, 0, 0);
976
+
977
+ // Measure geometry processing speed
978
+ let maxTriangles = 0;
979
+ let currentCount = 1000; // start with 1k triangles
980
+ let startTime = performance.now();
981
+ let lastStableCount = 0;
982
+ let stableFrames = 0;
983
+
984
+ function render() {
985
+ // Generate triangles (each triangle has 3 points)
986
+ const positions = new Float32Array(currentCount * 3 * 2);
987
+ for (let i = 0; i < currentCount * 3 * 2; i += 2) {
988
+ positions[i] = Math.random() * 2 - 1;
989
+ positions[i+1] = Math.random() * 2 - 1;
990
+ }
991
+
992
+ testGl.bufferData(testGl.ARRAY_BUFFER, positions, testGl.DYNAMIC_DRAW);
993
+
994
+ testGl.clear(testGl.COLOR_BUFFER_BIT);
995
+ testGl.drawArrays(testGl.TRIANGLES, 0, currentCount * 3);
996
+
997
+ // Check if we can increase the count
998
+ const frameTime = performance.now() - startTime;
999
+
1000
+ if (frameTime < 16) { // ~60fps
1001
+ currentCount *= 1.2;
1002
+ stableFrames = 0;
1003
+ } else if (frameTime < 33) { // ~30fps
1004
+ stableFrames++;
1005
+ if (stableFrames > 5) {
1006
+ lastStableCount = Math.round(currentCount);
1007
+ resolve(Math.round(lastStableCount / 1000)); // return in thousands
1008
+ return;
1009
+ }
1010
+ currentCount *= 1.05;
1011
+ } else { // below 30fps
1012
+ lastStableCount = Math.round(currentCount * 0.8);
1013
+ resolve(Math.round(lastStableCount / 1000)); // return in thousands
1014
+ return;
1015
+ }
1016
+
1017
+ startTime = performance.now();
1018
+ requestAnimationFrame(render);
1019
+ }
1020
+
1021
+ render();
1022
+ });
1023
+ }
1024
+
1025
+ async function runComputeTest() {
1026
+ return new Promise(resolve => {
1027
+ // This test uses WebGL to simulate general computation (like GPGPU)
1028
+ const testCanvas = document.createElement('canvas');
1029
+ testCanvas.width = 256;
1030
+ testCanvas.height = 256;
1031
+
1032
+ const testGl = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
1033
+ if (!testGl) {
1034
+ resolve(0);
1035
+ return;
1036
+ }
1037
+
1038
+ // Create texture for computation
1039
+ const texture = testGl.createTexture();
1040
+ testGl.bindTexture(testGl.TEXTURE_2D, texture);
1041
+ testGl.texParameteri(testGl.TEXTURE_2D, testGl.TEXTURE_MIN_FILTER, testGl.NEAREST);
1042
+ testGl.texParameteri(testGl.TEXTURE_2D, testGl.TEXTURE_MAG_FILTER, testGl.NEAREST);
1043
+ testGl.texParameteri(testGl.TEXTURE_2D, testGl.TEXTURE_WRAP_S, testGl.CLAMP_TO_EDGE);
1044
+ testGl.texParameteri(testGl.TEXTURE_2D, testGl.TEXTURE_WRAP_T, testGl.CLAMP_TO_EDGE);
1045
+
1046
+ const texSize = 256;
1047
+ testGl.texImage2D(testGl.TEXTURE_2D, 0, testGl.RGBA, texSize, texSize, 0, testGl.RGBA, testGl.UNSIGNED_BYTE, null);
1048
+
1049
+ // Create framebuffer
1050
+ const framebuffer = testGl.createFramebuffer();
1051
+ testGl.bindFramebuffer(testGl.FRAMEBUFFER, framebuffer);
1052
+ testGl.framebufferTexture2D(testGl.FRAMEBUFFER, testGl.COLOR_ATTACHMENT0, testGl.TEXTURE_2D, texture, 0);
1053
+
1054
+ // Compute shader (simulates many operations)
1055
+ const fsSource = `
1056
+ precision highp float;
1057
+
1058
+ uniform vec2 uResolution;
1059
+ uniform float uTime;
1060
+ uniform int uIterations;
1061
+
1062
+ void main() {
1063
+ vec2 uv = gl_FragCoord.xy / uResolution.xy;
1064
+
1065
+ // Complex computation simulation
1066
+ vec2 p = uv * 2.0 - 1.0;
1067
+ float v = 0.0;
1068
+
1069
+ for (int i = 0; i < 1024; i++) {
1070
+ if (i >= uIterations) break;
1071
+
1072
+ float fi = float(i);
1073
+ v += 0.01 * sin(
1074
+ p.x * sin(uTime * 0.2 + fi * 0.1) * 50.0 +
1075
+ p.y * sin(uTime * 0.3 + fi * 0.2) * 50.0 +
1076
+ uTime * 2.0
1077
+ );
1078
+
1079
+ p = vec2(
1080
+ sin(v) + p.x + cos(uTime + fi * 0.3),
1081
+ cos(v) + p.y + sin(uTime + fi * 0.4)
1082
+ );
1083
+ }
1084
+
1085
+ gl_FragColor = vec4(vec3(v), 1.0);
1086
+ }
1087
+ `;
1088
+
1089
+ const vsSource = `
1090
+ attribute vec2 aPosition;
1091
+ void main() {
1092
+ gl_Position = vec4(aPosition, 0.0, 1.0);
1093
+ }
1094
+ `;
1095
+
1096
+ const vertexShader = testGl.createShader(testGl.VERTEX_SHADER);
1097
+ testGl.shaderSource(vertexShader, vsSource);
1098
+ testGl.compileShader(vertexShader);
1099
+
1100
+ const fragmentShader = testGl.createShader(testGl.FRAGMENT_SHADER);
1101
+ testGl.shaderSource(fragmentShader, fsSource);
1102
+ testGl.compileShader(fragmentShader);
1103
+
1104
+ const shaderProgram = testGl.createProgram();
1105
+ testGl.attachShader(shaderProgram, vertexShader);
1106
+ testGl.attachShader(shaderProgram, fragmentShader);
1107
+ testGl.linkProgram(shaderProgram);
1108
+ testGl.useProgram(shaderProgram);
1109
+
1110
+ const positions = [-1, -1, 1, -1, -1, 1, 1, 1];
1111
+ const positionBuffer = testGl.createBuffer();
1112
+ testGl.bindBuffer(testGl.ARRAY_BUFFER, positionBuffer);
1113
+ testGl.bufferData(testGl.ARRAY_BUFFER, new Float32Array(positions), testGl.STATIC_DRAW);
1114
+
1115
+ const aPosition = testGl.getAttribLocation(shaderProgram, 'aPosition');
1116
+ testGl.enableVertexAttribArray(aPosition);
1117
+ testGl.vertexAttribPointer(aPosition, 2, testGl.FLOAT, false, 0, 0);
1118
+
1119
+ const uResolution = testGl.getUniformLocation(shaderProgram, 'uResolution');
1120
+ const uTime = testGl.getUniformLocation(shaderProgram, 'uTime');
1121
+ const uIterations = testGl.getUniformLocation(shaderProgram, 'uIterations');
1122
+
1123
+ // Find maximum iterations that can run at 60fps
1124
+ let iterations = 10;
1125
+ let lastStable = 0;
1126
+ let startTime = performance.now();
1127
+ let opsPerSecond = 0;
1128
+
1129
+ function render() {
1130
+ testGl.uniform2f(uResolution, texSize, texSize);
1131
+ testGl.uniform1f(uTime, (performance.now() - startTime) / 1000);
1132
+ testGl.uniform1i(uIterations, iterations);
1133
+
1134
+ testGl.viewport(0, 0, texSize, texSize);
1135
+ testGl.clear(testGl.COLOR_BUFFER_BIT);
1136
+ testGl.drawArrays(testGl.TRIANGLE_STRIP, 0, 4);
1137
+
1138
+ // Read back result to ensure computation happens
1139
+ const pixels = new Uint8Array(4);
1140
+ testGl.readPixels(0, 0, 1, 1, testGl.RGBA, testGl.UNSIGNED_BYTE, pixels);
1141
+
1142
+ // Measure frame time
1143
+ const frameTime = performance.now() - startTime;
1144
+
1145
+ if (frameTime < 16) { // ~60fps
1146
+ iterations = Math.floor(iterations * 1.3);
1147
+ } else {
1148
+ // Estimate ops per second
1149
+ opsPerSecond = Math.round(iterations * texSize * texSize * 60);
1150
+ resolve(opsPerSecond / 1000000); // return in millions
1151
+ return;
1152
+ }
1153
+
1154
+ startTime = performance.now();
1155
+ requestAnimationFrame(render);
1156
+ }
1157
+
1158
+ render();
1159
+ });
1160
+ }
1161
+
1162
+ // Event listeners
1163
+ runButton.addEventListener('click', runBenchmark);
1164
+
1165
+ // Share button
1166
+ document.getElementById('shareButton').addEventListener('click', function() {
1167
+ if (navigator.share) {
1168
+ navigator.share({
1169
+ title: 'My GPU Benchmark Results',
1170
+ text: `I scored ${finalScore.textContent} on the Mobile GPU Benchmark!`,
1171
+ url: window.location.href
1172
+ }).catch(err => {
1173
+ console.log('Error sharing:', err);
1174
+ fallbackShare();
1175
+ });
1176
+ } else {
1177
+ fallbackShare();
1178
+ }
1179
+ });
1180
+
1181
+ function fallbackShare() {
1182
+ const text = `My GPU Benchmark Score: ${finalScore.textContent}\n\nTest your device at: ${window.location.href}`;
1183
+
1184
+ if (navigator.clipboard) {
1185
+ navigator.clipboard.writeText(text).then(() => {
1186
+ alert('Results copied to clipboard!');
1187
+ }).catch(err => {
1188
+ prompt('Copy to clipboard:', text);
1189
+ });
1190
+ } else {
1191
+ prompt('Copy to clipboard:', text);
1192
+ }
1193
+ }
1194
+
1195
+ // Save button
1196
+ document.getElementById('saveButton').addEventListener('click', function() {
1197
+ alert('In a real app, this would save your results to local storage or export them.');
1198
+ });
1199
+ });
1200
+ </script>
1201
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
1202
+ </html>