Update index.html
Browse files- index.html +727 -402
index.html
CHANGED
@@ -1,13 +1,12 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
<html lang="en">
|
3 |
-
|
4 |
<head>
|
5 |
<meta charset="UTF-8">
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
-
<title>Interactive 3D GPU Threading</title>
|
8 |
<style>
|
9 |
body {
|
10 |
-
font-family: 'Consolas', '
|
11 |
background: #ffffff;
|
12 |
color: #000000;
|
13 |
margin: 0;
|
@@ -15,7 +14,7 @@
|
|
15 |
line-height: 1.6;
|
16 |
user-select: none;
|
17 |
}
|
18 |
-
|
19 |
.container {
|
20 |
max-width: 1400px;
|
21 |
margin: 0 auto;
|
@@ -23,7 +22,7 @@
|
|
23 |
border: 2px solid #000000;
|
24 |
padding: 30px;
|
25 |
}
|
26 |
-
|
27 |
h1 {
|
28 |
text-align: center;
|
29 |
margin-bottom: 40px;
|
@@ -33,7 +32,7 @@
|
|
33 |
border-bottom: 2px solid #000000;
|
34 |
padding-bottom: 15px;
|
35 |
}
|
36 |
-
|
37 |
.controls {
|
38 |
display: flex;
|
39 |
gap: 30px;
|
@@ -42,13 +41,13 @@
|
|
42 |
justify-content: center;
|
43 |
align-items: center;
|
44 |
}
|
45 |
-
|
46 |
.control-group {
|
47 |
background: #ffffff;
|
48 |
padding: 15px 20px;
|
49 |
border: 1px solid #000000;
|
50 |
}
|
51 |
-
|
52 |
.control-group label {
|
53 |
display: block;
|
54 |
margin-bottom: 10px;
|
@@ -57,13 +56,13 @@
|
|
57 |
text-align: center;
|
58 |
font-size: 14px;
|
59 |
}
|
60 |
-
|
61 |
.dimension-inputs {
|
62 |
display: flex;
|
63 |
gap: 10px;
|
64 |
align-items: center;
|
65 |
}
|
66 |
-
|
67 |
.dimension-inputs input {
|
68 |
width: 50px;
|
69 |
padding: 8px;
|
@@ -72,15 +71,15 @@
|
|
72 |
color: #000000;
|
73 |
font-weight: bold;
|
74 |
text-align: center;
|
75 |
-
font-family:
|
76 |
}
|
77 |
-
|
78 |
.dimension-inputs .label {
|
79 |
font-size: 14px;
|
80 |
color: #000000;
|
81 |
margin: 0 5px;
|
82 |
}
|
83 |
-
|
84 |
.stats {
|
85 |
display: flex;
|
86 |
gap: 20px;
|
@@ -88,7 +87,7 @@
|
|
88 |
flex-wrap: wrap;
|
89 |
justify-content: center;
|
90 |
}
|
91 |
-
|
92 |
.stat-box {
|
93 |
background: #ffffff;
|
94 |
padding: 15px 20px;
|
@@ -96,7 +95,7 @@
|
|
96 |
text-align: center;
|
97 |
min-width: 120px;
|
98 |
}
|
99 |
-
|
100 |
.stat-box .label {
|
101 |
font-size: 12px;
|
102 |
margin-bottom: 8px;
|
@@ -104,24 +103,24 @@
|
|
104 |
text-transform: uppercase;
|
105 |
letter-spacing: 1px;
|
106 |
}
|
107 |
-
|
108 |
.stat-box .value {
|
109 |
font-size: 24px;
|
110 |
font-weight: bold;
|
111 |
color: #000000;
|
112 |
}
|
113 |
-
|
114 |
.main-visualization {
|
115 |
display: flex;
|
116 |
gap: 40px;
|
117 |
margin-bottom: 40px;
|
118 |
}
|
119 |
-
|
120 |
.cube-section {
|
121 |
flex: 1;
|
122 |
min-width: 600px;
|
123 |
}
|
124 |
-
|
125 |
.section-title {
|
126 |
text-align: center;
|
127 |
margin-bottom: 20px;
|
@@ -131,7 +130,7 @@
|
|
131 |
border-bottom: 1px solid #000000;
|
132 |
padding-bottom: 10px;
|
133 |
}
|
134 |
-
|
135 |
.cube-container {
|
136 |
height: 500px;
|
137 |
perspective: 1200px;
|
@@ -144,17 +143,17 @@
|
|
144 |
cursor: grab;
|
145 |
overflow: hidden;
|
146 |
}
|
147 |
-
|
148 |
.cube-container:active {
|
149 |
cursor: grabbing;
|
150 |
}
|
151 |
-
|
152 |
.cube-3d {
|
153 |
position: relative;
|
154 |
transform-style: preserve-3d;
|
155 |
transition: transform 0.1s ease-out;
|
156 |
}
|
157 |
-
|
158 |
.block-cube {
|
159 |
position: absolute;
|
160 |
width: 70px;
|
@@ -171,7 +170,7 @@
|
|
171 |
text-align: center;
|
172 |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
173 |
}
|
174 |
-
|
175 |
.block-cube:hover {
|
176 |
background: #f0f0f0;
|
177 |
border-width: 3px;
|
@@ -179,7 +178,7 @@
|
|
179 |
z-index: 10;
|
180 |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
181 |
}
|
182 |
-
|
183 |
.block-cube.selected {
|
184 |
background: #000000;
|
185 |
color: #ffffff;
|
@@ -187,7 +186,7 @@
|
|
187 |
z-index: 5;
|
188 |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
189 |
}
|
190 |
-
|
191 |
.block-label {
|
192 |
font-size: 11px;
|
193 |
line-height: 1.2;
|
@@ -196,24 +195,24 @@
|
|
196 |
color: #000000;
|
197 |
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
|
198 |
}
|
199 |
-
|
200 |
.block-cube.selected .block-label {
|
201 |
color: #ffffff;
|
202 |
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
|
203 |
}
|
204 |
-
|
205 |
.thread-count {
|
206 |
font-size: 9px;
|
207 |
color: #333333;
|
208 |
font-weight: bold;
|
209 |
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
|
210 |
}
|
211 |
-
|
212 |
.block-cube.selected .thread-count {
|
213 |
color: #cccccc;
|
214 |
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
|
215 |
}
|
216 |
-
|
217 |
.info-panel {
|
218 |
flex: 0 0 350px;
|
219 |
background: #ffffff;
|
@@ -222,7 +221,7 @@
|
|
222 |
max-height: 600px;
|
223 |
overflow-y: auto;
|
224 |
}
|
225 |
-
|
226 |
.info-title {
|
227 |
font-size: 18px;
|
228 |
font-weight: bold;
|
@@ -232,14 +231,14 @@
|
|
232 |
border-bottom: 1px solid #000000;
|
233 |
padding-bottom: 10px;
|
234 |
}
|
235 |
-
|
236 |
.info-item {
|
237 |
margin-bottom: 20px;
|
238 |
padding: 15px;
|
239 |
background: #f8f8f8;
|
240 |
border-left: 4px solid #000000;
|
241 |
}
|
242 |
-
|
243 |
.info-item .label {
|
244 |
font-size: 12px;
|
245 |
margin-bottom: 8px;
|
@@ -247,47 +246,48 @@
|
|
247 |
text-transform: uppercase;
|
248 |
letter-spacing: 1px;
|
249 |
}
|
250 |
-
|
251 |
.info-item .value {
|
252 |
font-size: 16px;
|
253 |
font-weight: bold;
|
254 |
color: #000000;
|
255 |
}
|
256 |
-
|
257 |
.thread-detail-grid {
|
258 |
display: grid;
|
259 |
-
grid-template-columns: repeat(auto-fit, minmax(
|
260 |
-
gap:
|
261 |
margin-top: 15px;
|
262 |
-
padding:
|
263 |
background: #f0f0f0;
|
264 |
border: 1px solid #cccccc;
|
265 |
}
|
266 |
-
|
267 |
.thread-detail {
|
268 |
-
width:
|
269 |
-
height:
|
270 |
background: #ffffff;
|
271 |
border: 1px solid #000000;
|
272 |
display: flex;
|
273 |
align-items: center;
|
274 |
justify-content: center;
|
275 |
-
font-size:
|
|
|
276 |
cursor: pointer;
|
277 |
transition: all 0.2s ease;
|
278 |
}
|
279 |
-
|
280 |
.thread-detail:hover {
|
281 |
background: #000000;
|
282 |
color: #ffffff;
|
283 |
transform: scale(1.2);
|
284 |
}
|
285 |
-
|
286 |
.thread-detail.selected {
|
287 |
background: #000000;
|
288 |
color: #ffffff;
|
289 |
}
|
290 |
-
|
291 |
.instructions {
|
292 |
text-align: center;
|
293 |
margin-top: 20px;
|
@@ -297,7 +297,7 @@
|
|
297 |
padding: 15px;
|
298 |
background: #f8f8f8;
|
299 |
}
|
300 |
-
|
301 |
.interaction-guide {
|
302 |
display: flex;
|
303 |
justify-content: space-around;
|
@@ -307,50 +307,355 @@
|
|
307 |
border: 1px solid #cccccc;
|
308 |
font-size: 12px;
|
309 |
}
|
310 |
-
|
311 |
.interaction-item {
|
312 |
text-align: center;
|
313 |
color: #333333;
|
314 |
}
|
315 |
-
|
316 |
.interaction-item .icon {
|
317 |
font-size: 18px;
|
318 |
margin-bottom: 5px;
|
319 |
display: block;
|
320 |
}
|
321 |
-
|
322 |
.spatial-explanation {
|
323 |
background: #f8f8f8;
|
324 |
padding: 25px;
|
325 |
border: 2px solid #000000;
|
326 |
margin-bottom: 30px;
|
327 |
}
|
328 |
-
|
329 |
.spatial-explanation h3 {
|
330 |
color: #000000;
|
331 |
margin-bottom: 20px;
|
332 |
border-bottom: 1px solid #000000;
|
333 |
padding-bottom: 10px;
|
334 |
}
|
335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
.code-example {
|
337 |
background: #f0f0f0;
|
338 |
padding: 20px;
|
339 |
border: 1px solid #cccccc;
|
340 |
-
font-family: '
|
341 |
font-size: 13px;
|
342 |
line-height: 1.6;
|
343 |
border-left: 4px solid #000000;
|
344 |
margin: 15px 0;
|
345 |
color: #000000;
|
346 |
}
|
347 |
-
|
348 |
.highlight {
|
349 |
background: #e0e0e0;
|
350 |
padding: 2px 4px;
|
351 |
border: 1px solid #000000;
|
352 |
}
|
353 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
354 |
.current-view {
|
355 |
position: absolute;
|
356 |
top: 10px;
|
@@ -363,11 +668,10 @@
|
|
363 |
}
|
364 |
</style>
|
365 |
</head>
|
366 |
-
|
367 |
<body>
|
368 |
<div class="container">
|
369 |
<h1>Interactive 3D GPU Threading</h1>
|
370 |
-
|
371 |
<div class="controls">
|
372 |
<div class="control-group">
|
373 |
<label>Grid Dimensions (X × Y × Z)</label>
|
@@ -390,7 +694,7 @@
|
|
390 |
</div>
|
391 |
</div>
|
392 |
</div>
|
393 |
-
|
394 |
<div class="stats">
|
395 |
<div class="stat-box">
|
396 |
<div class="label">Total Blocks</div>
|
@@ -409,7 +713,7 @@
|
|
409 |
<div class="value" id="spatialDims">3×2×2</div>
|
410 |
</div>
|
411 |
</div>
|
412 |
-
|
413 |
<div class="interaction-guide">
|
414 |
<div class="interaction-item">
|
415 |
<span class="icon">🖱️</span>
|
@@ -428,238 +732,237 @@
|
|
428 |
<div>Double-Click to Reset View</div>
|
429 |
</div>
|
430 |
</div>
|
431 |
-
|
432 |
<div class="main-visualization">
|
433 |
<div class="cube-section">
|
434 |
<div class="section-title">Interactive 3D Block Grid</div>
|
435 |
-
|
436 |
<div class="cube-container" id="cubeContainer">
|
437 |
<div class="cube-3d" id="cube3d"></div>
|
438 |
<div class="current-view" id="currentView">
|
439 |
Rotation: X=15° Y=25° | Zoom: 1.0x
|
440 |
</div>
|
441 |
</div>
|
442 |
-
|
443 |
<div class="instructions">
|
444 |
-
Drag to rotate the cube and explore 3D spatial relationships • Scroll to zoom in/out • Click blocks
|
445 |
-
to examine their threads • Double-click to reset view
|
446 |
</div>
|
447 |
</div>
|
448 |
-
|
449 |
<div class="info-panel">
|
450 |
<div class="info-title">Block & Thread Details</div>
|
451 |
<div id="blockInfo">
|
452 |
-
<div
|
453 |
-
|
454 |
-
Rotate the cube to explore spatial relationships, then click on any block to see its thread
|
455 |
-
organization and understand how neighboring blocks relate to each other in 3D space.
|
456 |
</div>
|
457 |
</div>
|
458 |
</div>
|
459 |
</div>
|
460 |
-
|
461 |
<div class="spatial-explanation">
|
462 |
<h3>Understanding Spatial Relationships Through Direct Manipulation</h3>
|
463 |
-
<p>The ability to directly manipulate the 3D cube with your cursor creates a much deeper understanding of
|
464 |
-
|
465 |
-
exactly which blocks are neighbors and how the grid extends in all three dimensions simultaneously.</p>
|
466 |
-
|
467 |
<p><strong>Why Direct Interaction Builds Better Mental Models:</strong></p>
|
468 |
-
<p>When you drag to rotate the cube, your brain naturally builds a spatial mental model that connects the
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
<p><strong>Learning Exercise:</strong> Try rotating the cube to view it from different angles while thinking
|
474 |
-
about how data flows through your algorithm. Can you identify which blocks would naturally work together
|
475 |
-
on shared data? The spatial arrangement you see directly represents the memory access patterns that will
|
476 |
-
determine your algorithm's performance.</p>
|
477 |
-
|
478 |
<div class="code-example">
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
|
|
|
|
|
|
|
|
|
|
499 |
</div>
|
|
|
|
|
500 |
</div>
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
}
|
557 |
}
|
558 |
-
|
559 |
-
updateCubeTransform();
|
560 |
-
}
|
561 |
-
|
562 |
-
function createBlock3D(blockX_idx, blockY_idx, blockZ_idx, blockDimX, blockDimY, blockDimZ, spacing) {
|
563 |
-
const block = document.createElement('div');
|
564 |
-
block.className = 'block-cube';
|
565 |
-
|
566 |
-
// Calculate center offset for better visual balance
|
567 |
-
const gridX = parseInt(document.getElementById('gridX').value);
|
568 |
-
const gridY = parseInt(document.getElementById('gridY').value);
|
569 |
-
const gridZ = parseInt(document.getElementById('gridZ').value);
|
570 |
-
|
571 |
-
const centerOffsetX = (gridX - 1) * spacing / 2;
|
572 |
-
const centerOffsetY = (gridY - 1) * spacing / 2;
|
573 |
-
const centerOffsetZ = (gridZ - 1) * spacing / 2;
|
574 |
-
|
575 |
-
// Position in 3D space
|
576 |
-
const translateX = blockX_idx * spacing - centerOffsetX;
|
577 |
-
const translateY = blockY_idx * spacing - centerOffsetY;
|
578 |
-
const translateZ = blockZ_idx * spacing - centerOffsetZ;
|
579 |
-
|
580 |
-
block.style.transform = `translate3d(${translateX}px, ${translateY}px, ${translateZ}px)`;
|
581 |
-
|
582 |
-
// Block content
|
583 |
-
const label = document.createElement('div');
|
584 |
-
label.className = 'block-label';
|
585 |
-
label.textContent = `[${blockX_idx},${blockY_idx},${blockZ_idx}]`;
|
586 |
-
block.appendChild(label);
|
587 |
-
|
588 |
-
const threadCount = document.createElement('div');
|
589 |
-
threadCount.className = 'thread-count';
|
590 |
-
threadCount.textContent = `${blockDimX}×${blockDimY}×${blockDimZ}`;
|
591 |
-
block.appendChild(threadCount);
|
592 |
-
|
593 |
-
// Click handler
|
594 |
-
block.addEventListener('click', (e) => {
|
595 |
-
e.stopPropagation();
|
596 |
-
|
597 |
-
// Remove previous selection
|
598 |
-
document.querySelectorAll('.block-cube.selected').forEach(b => b.classList.remove('selected'));
|
599 |
-
|
600 |
-
// Select this block
|
601 |
-
block.classList.add('selected');
|
602 |
-
selectedBlock = {
|
603 |
-
idx: { x: blockX_idx, y: blockY_idx, z: blockZ_idx },
|
604 |
-
dim: { x: blockDimX, y: blockDimY, z: blockDimZ }
|
605 |
-
};
|
606 |
-
|
607 |
-
updateBlockInfo();
|
608 |
-
});
|
609 |
-
|
610 |
-
return block;
|
611 |
}
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
627 |
<div style="text-align: center; color: #666666; padding: 25px; border: 1px solid #cccccc; background: #f8f8f8;">
|
628 |
Rotate the cube to explore spatial relationships, then click on any block to see its thread organization and understand how neighboring blocks relate to each other in 3D space.
|
629 |
</div>
|
630 |
`;
|
631 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
632 |
}
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
// Calculate spatial neighbors
|
638 |
-
const gridX = parseInt(document.getElementById('gridX').value);
|
639 |
-
const gridY = parseInt(document.getElementById('gridY').value);
|
640 |
-
const gridZ = parseInt(document.getElementById('gridZ').value);
|
641 |
-
|
642 |
-
const neighbors = [];
|
643 |
-
const directions = [
|
644 |
-
{ dx: -1, dy: 0, dz: 0, name: 'left' },
|
645 |
-
{ dx: 1, dy: 0, dz: 0, name: 'right' },
|
646 |
-
{ dx: 0, dy: -1, dz: 0, name: 'back' },
|
647 |
-
{ dx: 0, dy: 1, dz: 0, name: 'front' },
|
648 |
-
{ dx: 0, dy: 0, dz: -1, name: 'below' },
|
649 |
-
{ dx: 0, dy: 0, dz: 1, name: 'above' }
|
650 |
-
];
|
651 |
-
|
652 |
-
directions.forEach(dir => {
|
653 |
-
const nx = b.idx.x + dir.dx;
|
654 |
-
const ny = b.idx.y + dir.dy;
|
655 |
-
const nz = b.idx.z + dir.dz;
|
656 |
-
|
657 |
-
if (nx >= 0 && nx < gridX && ny >= 0 && ny < gridY && nz >= 0 && nz < gridZ) {
|
658 |
-
neighbors.push(`[${nx}, ${ny}, ${nz}] (${dir.name})`);
|
659 |
-
}
|
660 |
-
});
|
661 |
-
|
662 |
-
blockInfo.innerHTML = `
|
663 |
<div class="info-item">
|
664 |
<div class="label">Selected Block</div>
|
665 |
<div class="value">Block [${b.idx.x}, ${b.idx.y}, ${b.idx.z}]</div>
|
@@ -671,8 +974,14 @@
|
|
671 |
</div>
|
672 |
|
673 |
<div class="info-item">
|
674 |
-
<div class="label">
|
675 |
-
<div class="value">
|
|
|
|
|
|
|
|
|
|
|
|
|
676 |
</div>
|
677 |
|
678 |
<div style="margin-top: 20px;">
|
@@ -690,77 +999,93 @@
|
|
690 |
|
691 |
<div style="margin-top: 15px; padding: 15px; background: #e8e8e8; border: 1px solid #cccccc;">
|
692 |
<div style="font-size: 11px; color: #333333;">
|
693 |
-
💡 <strong>
|
694 |
</div>
|
695 |
</div>
|
696 |
`;
|
697 |
-
|
698 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
699 |
}
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
|
|
715 |
<div style="text-align: center; padding: 20px; color: #666666;">
|
716 |
Too many threads to display (${totalThreads} total)<br>
|
717 |
Try smaller block dimensions to see individual threads
|
718 |
</div>
|
719 |
`;
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
}
|
755 |
}
|
756 |
}
|
757 |
}
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
|
|
764 |
<div style="margin-top: 20px; padding: 15px; background: #e8e8e8; border: 1px solid #000000;">
|
765 |
<div style="font-size: 12px; font-weight: bold; margin-bottom: 10px;">Selected Thread Details:</div>
|
766 |
<div style="font-size: 11px; line-height: 1.6;">
|
@@ -774,90 +1099,90 @@
|
|
774 |
</div>
|
775 |
</div>
|
776 |
`;
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
});
|
800 |
-
|
801 |
-
// Mouse move event
|
802 |
-
document.addEventListener('mousemove', (e) => {
|
803 |
-
if (!isMouseDown) return;
|
804 |
-
|
805 |
-
const deltaX = e.clientX - lastMouseX;
|
806 |
-
const deltaY = e.clientY - lastMouseY;
|
807 |
-
|
808 |
-
// Update rotation based on mouse movement
|
809 |
-
rotationY += deltaX * 0.5; // Horizontal movement controls Y rotation
|
810 |
-
rotationX -= deltaY * 0.5; // Vertical movement controls X rotation (inverted for natural feel)
|
811 |
-
|
812 |
-
// Constrain rotations to reasonable ranges
|
813 |
-
rotationX = Math.max(-90, Math.min(90, rotationX));
|
814 |
-
|
815 |
-
updateCubeTransform();
|
816 |
-
|
817 |
-
lastMouseX = e.clientX;
|
818 |
-
lastMouseY = e.clientY;
|
819 |
-
});
|
820 |
-
|
821 |
-
// Mouse up event
|
822 |
-
document.addEventListener('mouseup', () => {
|
823 |
-
isMouseDown = false;
|
824 |
-
cubeContainer.style.cursor = 'grab';
|
825 |
-
});
|
826 |
-
|
827 |
-
// Mouse wheel event for zooming
|
828 |
-
cubeContainer.addEventListener('wheel', (e) => {
|
829 |
-
e.preventDefault();
|
830 |
-
|
831 |
-
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
832 |
-
zoom *= zoomFactor;
|
833 |
-
zoom = Math.max(0.3, Math.min(3.0, zoom)); // Constrain zoom range
|
834 |
-
|
835 |
-
updateCubeTransform();
|
836 |
-
});
|
837 |
-
|
838 |
-
// Double-click to reset view
|
839 |
-
cubeContainer.addEventListener('dblclick', () => {
|
840 |
-
rotationX = 15;
|
841 |
-
rotationY = 25;
|
842 |
-
zoom = 1.0;
|
843 |
-
updateCubeTransform();
|
844 |
-
});
|
845 |
-
|
846 |
-
// Prevent context menu on right-click
|
847 |
-
cubeContainer.addEventListener('contextmenu', (e) => {
|
848 |
-
e.preventDefault();
|
849 |
-
});
|
850 |
-
}
|
851 |
-
|
852 |
-
// Event listeners for dimension controls
|
853 |
-
['gridX', 'gridY', 'gridZ', 'blockX', 'blockY', 'blockZ'].forEach(id => {
|
854 |
-
document.getElementById(id).addEventListener('input', updateVisualization);
|
855 |
});
|
856 |
-
|
857 |
-
//
|
858 |
-
|
859 |
-
|
860 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
861 |
</body>
|
862 |
-
|
863 |
</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>Interactive 3D GPU Threading Cube</title>
|
7 |
<style>
|
8 |
body {
|
9 |
+
font-family: 'Consolas', 'Courier New', monospace;
|
10 |
background: #ffffff;
|
11 |
color: #000000;
|
12 |
margin: 0;
|
|
|
14 |
line-height: 1.6;
|
15 |
user-select: none;
|
16 |
}
|
17 |
+
|
18 |
.container {
|
19 |
max-width: 1400px;
|
20 |
margin: 0 auto;
|
|
|
22 |
border: 2px solid #000000;
|
23 |
padding: 30px;
|
24 |
}
|
25 |
+
|
26 |
h1 {
|
27 |
text-align: center;
|
28 |
margin-bottom: 40px;
|
|
|
32 |
border-bottom: 2px solid #000000;
|
33 |
padding-bottom: 15px;
|
34 |
}
|
35 |
+
|
36 |
.controls {
|
37 |
display: flex;
|
38 |
gap: 30px;
|
|
|
41 |
justify-content: center;
|
42 |
align-items: center;
|
43 |
}
|
44 |
+
|
45 |
.control-group {
|
46 |
background: #ffffff;
|
47 |
padding: 15px 20px;
|
48 |
border: 1px solid #000000;
|
49 |
}
|
50 |
+
|
51 |
.control-group label {
|
52 |
display: block;
|
53 |
margin-bottom: 10px;
|
|
|
56 |
text-align: center;
|
57 |
font-size: 14px;
|
58 |
}
|
59 |
+
|
60 |
.dimension-inputs {
|
61 |
display: flex;
|
62 |
gap: 10px;
|
63 |
align-items: center;
|
64 |
}
|
65 |
+
|
66 |
.dimension-inputs input {
|
67 |
width: 50px;
|
68 |
padding: 8px;
|
|
|
71 |
color: #000000;
|
72 |
font-weight: bold;
|
73 |
text-align: center;
|
74 |
+
font-family: 'Consolas', 'Courier New', monospace;
|
75 |
}
|
76 |
+
|
77 |
.dimension-inputs .label {
|
78 |
font-size: 14px;
|
79 |
color: #000000;
|
80 |
margin: 0 5px;
|
81 |
}
|
82 |
+
|
83 |
.stats {
|
84 |
display: flex;
|
85 |
gap: 20px;
|
|
|
87 |
flex-wrap: wrap;
|
88 |
justify-content: center;
|
89 |
}
|
90 |
+
|
91 |
.stat-box {
|
92 |
background: #ffffff;
|
93 |
padding: 15px 20px;
|
|
|
95 |
text-align: center;
|
96 |
min-width: 120px;
|
97 |
}
|
98 |
+
|
99 |
.stat-box .label {
|
100 |
font-size: 12px;
|
101 |
margin-bottom: 8px;
|
|
|
103 |
text-transform: uppercase;
|
104 |
letter-spacing: 1px;
|
105 |
}
|
106 |
+
|
107 |
.stat-box .value {
|
108 |
font-size: 24px;
|
109 |
font-weight: bold;
|
110 |
color: #000000;
|
111 |
}
|
112 |
+
|
113 |
.main-visualization {
|
114 |
display: flex;
|
115 |
gap: 40px;
|
116 |
margin-bottom: 40px;
|
117 |
}
|
118 |
+
|
119 |
.cube-section {
|
120 |
flex: 1;
|
121 |
min-width: 600px;
|
122 |
}
|
123 |
+
|
124 |
.section-title {
|
125 |
text-align: center;
|
126 |
margin-bottom: 20px;
|
|
|
130 |
border-bottom: 1px solid #000000;
|
131 |
padding-bottom: 10px;
|
132 |
}
|
133 |
+
|
134 |
.cube-container {
|
135 |
height: 500px;
|
136 |
perspective: 1200px;
|
|
|
143 |
cursor: grab;
|
144 |
overflow: hidden;
|
145 |
}
|
146 |
+
|
147 |
.cube-container:active {
|
148 |
cursor: grabbing;
|
149 |
}
|
150 |
+
|
151 |
.cube-3d {
|
152 |
position: relative;
|
153 |
transform-style: preserve-3d;
|
154 |
transition: transform 0.1s ease-out;
|
155 |
}
|
156 |
+
|
157 |
.block-cube {
|
158 |
position: absolute;
|
159 |
width: 70px;
|
|
|
170 |
text-align: center;
|
171 |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
172 |
}
|
173 |
+
|
174 |
.block-cube:hover {
|
175 |
background: #f0f0f0;
|
176 |
border-width: 3px;
|
|
|
178 |
z-index: 10;
|
179 |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
180 |
}
|
181 |
+
|
182 |
.block-cube.selected {
|
183 |
background: #000000;
|
184 |
color: #ffffff;
|
|
|
186 |
z-index: 5;
|
187 |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
188 |
}
|
189 |
+
|
190 |
.block-label {
|
191 |
font-size: 11px;
|
192 |
line-height: 1.2;
|
|
|
195 |
color: #000000;
|
196 |
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
|
197 |
}
|
198 |
+
|
199 |
.block-cube.selected .block-label {
|
200 |
color: #ffffff;
|
201 |
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
|
202 |
}
|
203 |
+
|
204 |
.thread-count {
|
205 |
font-size: 9px;
|
206 |
color: #333333;
|
207 |
font-weight: bold;
|
208 |
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
|
209 |
}
|
210 |
+
|
211 |
.block-cube.selected .thread-count {
|
212 |
color: #cccccc;
|
213 |
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
|
214 |
}
|
215 |
+
|
216 |
.info-panel {
|
217 |
flex: 0 0 350px;
|
218 |
background: #ffffff;
|
|
|
221 |
max-height: 600px;
|
222 |
overflow-y: auto;
|
223 |
}
|
224 |
+
|
225 |
.info-title {
|
226 |
font-size: 18px;
|
227 |
font-weight: bold;
|
|
|
231 |
border-bottom: 1px solid #000000;
|
232 |
padding-bottom: 10px;
|
233 |
}
|
234 |
+
|
235 |
.info-item {
|
236 |
margin-bottom: 20px;
|
237 |
padding: 15px;
|
238 |
background: #f8f8f8;
|
239 |
border-left: 4px solid #000000;
|
240 |
}
|
241 |
+
|
242 |
.info-item .label {
|
243 |
font-size: 12px;
|
244 |
margin-bottom: 8px;
|
|
|
246 |
text-transform: uppercase;
|
247 |
letter-spacing: 1px;
|
248 |
}
|
249 |
+
|
250 |
.info-item .value {
|
251 |
font-size: 16px;
|
252 |
font-weight: bold;
|
253 |
color: #000000;
|
254 |
}
|
255 |
+
|
256 |
.thread-detail-grid {
|
257 |
display: grid;
|
258 |
+
grid-template-columns: repeat(auto-fit, minmax(26px, 1fr));
|
259 |
+
gap: 1px;
|
260 |
margin-top: 15px;
|
261 |
+
padding: 12px;
|
262 |
background: #f0f0f0;
|
263 |
border: 1px solid #cccccc;
|
264 |
}
|
265 |
+
|
266 |
.thread-detail {
|
267 |
+
width: 24px;
|
268 |
+
height: 24px;
|
269 |
background: #ffffff;
|
270 |
border: 1px solid #000000;
|
271 |
display: flex;
|
272 |
align-items: center;
|
273 |
justify-content: center;
|
274 |
+
font-size: 8px;
|
275 |
+
font-weight: bold;
|
276 |
cursor: pointer;
|
277 |
transition: all 0.2s ease;
|
278 |
}
|
279 |
+
|
280 |
.thread-detail:hover {
|
281 |
background: #000000;
|
282 |
color: #ffffff;
|
283 |
transform: scale(1.2);
|
284 |
}
|
285 |
+
|
286 |
.thread-detail.selected {
|
287 |
background: #000000;
|
288 |
color: #ffffff;
|
289 |
}
|
290 |
+
|
291 |
.instructions {
|
292 |
text-align: center;
|
293 |
margin-top: 20px;
|
|
|
297 |
padding: 15px;
|
298 |
background: #f8f8f8;
|
299 |
}
|
300 |
+
|
301 |
.interaction-guide {
|
302 |
display: flex;
|
303 |
justify-content: space-around;
|
|
|
307 |
border: 1px solid #cccccc;
|
308 |
font-size: 12px;
|
309 |
}
|
310 |
+
|
311 |
.interaction-item {
|
312 |
text-align: center;
|
313 |
color: #333333;
|
314 |
}
|
315 |
+
|
316 |
.interaction-item .icon {
|
317 |
font-size: 18px;
|
318 |
margin-bottom: 5px;
|
319 |
display: block;
|
320 |
}
|
321 |
+
|
322 |
.spatial-explanation {
|
323 |
background: #f8f8f8;
|
324 |
padding: 25px;
|
325 |
border: 2px solid #000000;
|
326 |
margin-bottom: 30px;
|
327 |
}
|
328 |
+
|
329 |
.spatial-explanation h3 {
|
330 |
color: #000000;
|
331 |
margin-bottom: 20px;
|
332 |
border-bottom: 1px solid #000000;
|
333 |
padding-bottom: 10px;
|
334 |
}
|
335 |
+
|
336 |
+
.warp-visualization {
|
337 |
+
background: #ffffff;
|
338 |
+
border: 1px solid #000000;
|
339 |
+
padding: 20px;
|
340 |
+
margin: 20px 0;
|
341 |
+
display: flex;
|
342 |
+
flex-direction: column;
|
343 |
+
align-items: center;
|
344 |
+
}
|
345 |
+
|
346 |
+
.sm-representation {
|
347 |
+
background: #f0f0f0;
|
348 |
+
border: 2px solid #000000;
|
349 |
+
padding: 15px;
|
350 |
+
margin: 10px 0;
|
351 |
+
width: 100%;
|
352 |
+
max-width: 600px;
|
353 |
+
}
|
354 |
+
|
355 |
+
.sm-title {
|
356 |
+
font-weight: bold;
|
357 |
+
text-align: center;
|
358 |
+
margin-bottom: 15px;
|
359 |
+
font-size: 14px;
|
360 |
+
border-bottom: 1px solid #000000;
|
361 |
+
padding-bottom: 5px;
|
362 |
+
}
|
363 |
+
|
364 |
+
.warp-grid {
|
365 |
+
display: grid;
|
366 |
+
grid-template-columns: repeat(8, 1fr);
|
367 |
+
gap: 2px;
|
368 |
+
margin-bottom: 15px;
|
369 |
+
}
|
370 |
+
|
371 |
+
.warp-thread {
|
372 |
+
width: 20px;
|
373 |
+
height: 20px;
|
374 |
+
background: #ffffff;
|
375 |
+
border: 1px solid #000000;
|
376 |
+
display: flex;
|
377 |
+
align-items: center;
|
378 |
+
justify-content: center;
|
379 |
+
font-size: 8px;
|
380 |
+
font-weight: bold;
|
381 |
+
}
|
382 |
+
|
383 |
+
.warp-0 { background: #e0e0e0; }
|
384 |
+
.warp-1 { background: #d0d0d0; }
|
385 |
+
.warp-2 { background: #c0c0c0; }
|
386 |
+
.warp-3 { background: #b0b0b0; }
|
387 |
+
|
388 |
+
.warp-label {
|
389 |
+
font-size: 12px;
|
390 |
+
font-weight: bold;
|
391 |
+
margin-bottom: 5px;
|
392 |
+
text-align: center;
|
393 |
+
}
|
394 |
+
|
395 |
.code-example {
|
396 |
background: #f0f0f0;
|
397 |
padding: 20px;
|
398 |
border: 1px solid #cccccc;
|
399 |
+
font-family: 'Consolas', 'Courier New', monospace;
|
400 |
font-size: 13px;
|
401 |
line-height: 1.6;
|
402 |
border-left: 4px solid #000000;
|
403 |
margin: 15px 0;
|
404 |
color: #000000;
|
405 |
}
|
406 |
+
|
407 |
.highlight {
|
408 |
background: #e0e0e0;
|
409 |
padding: 2px 4px;
|
410 |
border: 1px solid #000000;
|
411 |
}
|
412 |
+
|
413 |
+
.hardware-pipeline {
|
414 |
+
background: #ffffff;
|
415 |
+
border: 2px solid #000000;
|
416 |
+
padding: 20px;
|
417 |
+
margin: 20px 0;
|
418 |
+
}
|
419 |
+
|
420 |
+
.pipeline-stage {
|
421 |
+
margin-bottom: 30px;
|
422 |
+
border-bottom: 1px solid #cccccc;
|
423 |
+
padding-bottom: 20px;
|
424 |
+
}
|
425 |
+
|
426 |
+
.pipeline-stage h4 {
|
427 |
+
font-size: 16px;
|
428 |
+
font-weight: bold;
|
429 |
+
margin-bottom: 15px;
|
430 |
+
color: #000000;
|
431 |
+
border-bottom: 1px solid #000000;
|
432 |
+
padding-bottom: 8px;
|
433 |
+
}
|
434 |
+
|
435 |
+
.sm-array {
|
436 |
+
display: flex;
|
437 |
+
gap: 15px;
|
438 |
+
flex-wrap: wrap;
|
439 |
+
justify-content: center;
|
440 |
+
margin: 20px 0;
|
441 |
+
}
|
442 |
+
|
443 |
+
.sm-unit {
|
444 |
+
background: #f8f8f8;
|
445 |
+
border: 2px solid #000000;
|
446 |
+
padding: 15px;
|
447 |
+
min-width: 150px;
|
448 |
+
text-align: center;
|
449 |
+
}
|
450 |
+
|
451 |
+
.sm-header {
|
452 |
+
font-weight: bold;
|
453 |
+
font-size: 14px;
|
454 |
+
margin-bottom: 10px;
|
455 |
+
background: #000000;
|
456 |
+
color: #ffffff;
|
457 |
+
padding: 5px;
|
458 |
+
}
|
459 |
+
|
460 |
+
.block-assignment {
|
461 |
+
display: flex;
|
462 |
+
flex-direction: column;
|
463 |
+
gap: 5px;
|
464 |
+
}
|
465 |
+
|
466 |
+
.assigned-block {
|
467 |
+
background: #ffffff;
|
468 |
+
border: 1px solid #000000;
|
469 |
+
padding: 5px;
|
470 |
+
font-size: 11px;
|
471 |
+
font-weight: bold;
|
472 |
+
}
|
473 |
+
|
474 |
+
.sm-detailed {
|
475 |
+
background: #f8f8f8;
|
476 |
+
border: 2px solid #000000;
|
477 |
+
padding: 20px;
|
478 |
+
margin: 20px 0;
|
479 |
+
}
|
480 |
+
|
481 |
+
.sm-title {
|
482 |
+
font-size: 14px;
|
483 |
+
font-weight: bold;
|
484 |
+
text-align: center;
|
485 |
+
margin-bottom: 20px;
|
486 |
+
background: #000000;
|
487 |
+
color: #ffffff;
|
488 |
+
padding: 10px;
|
489 |
+
}
|
490 |
+
|
491 |
+
.sm-components {
|
492 |
+
display: flex;
|
493 |
+
flex-direction: column;
|
494 |
+
gap: 15px;
|
495 |
+
}
|
496 |
+
|
497 |
+
.scheduler-layer {
|
498 |
+
display: flex;
|
499 |
+
gap: 10px;
|
500 |
+
justify-content: center;
|
501 |
+
}
|
502 |
+
|
503 |
+
.scheduler-unit {
|
504 |
+
background: #d0d0d0;
|
505 |
+
border: 1px solid #000000;
|
506 |
+
padding: 8px 15px;
|
507 |
+
font-size: 11px;
|
508 |
+
font-weight: bold;
|
509 |
+
text-align: center;
|
510 |
+
min-width: 80px;
|
511 |
+
}
|
512 |
+
|
513 |
+
.register-layer {
|
514 |
+
display: flex;
|
515 |
+
gap: 10px;
|
516 |
+
justify-content: center;
|
517 |
+
}
|
518 |
+
|
519 |
+
.register-file {
|
520 |
+
background: #e0e0e0;
|
521 |
+
border: 1px solid #000000;
|
522 |
+
padding: 8px 15px;
|
523 |
+
font-size: 11px;
|
524 |
+
font-weight: bold;
|
525 |
+
text-align: center;
|
526 |
+
min-width: 80px;
|
527 |
+
}
|
528 |
+
|
529 |
+
.execution-layer {
|
530 |
+
display: flex;
|
531 |
+
gap: 8px;
|
532 |
+
justify-content: center;
|
533 |
+
flex-wrap: wrap;
|
534 |
+
}
|
535 |
+
|
536 |
+
.execution-unit {
|
537 |
+
border: 1px solid #000000;
|
538 |
+
padding: 8px 12px;
|
539 |
+
font-size: 10px;
|
540 |
+
font-weight: bold;
|
541 |
+
text-align: center;
|
542 |
+
min-width: 50px;
|
543 |
+
}
|
544 |
+
|
545 |
+
.execution-unit.int32 {
|
546 |
+
background: #f0f0f0;
|
547 |
+
}
|
548 |
+
|
549 |
+
.execution-unit.fp32 {
|
550 |
+
background: #e8e8e8;
|
551 |
+
}
|
552 |
+
|
553 |
+
.execution-unit.tensor {
|
554 |
+
background: #d8d8d8;
|
555 |
+
}
|
556 |
+
|
557 |
+
.execution-unit.ldst {
|
558 |
+
background: #c8c8c8;
|
559 |
+
}
|
560 |
+
|
561 |
+
.execution-unit.sfu {
|
562 |
+
background: #b8b8b8;
|
563 |
+
}
|
564 |
+
|
565 |
+
.memory-layer {
|
566 |
+
display: flex;
|
567 |
+
justify-content: center;
|
568 |
+
}
|
569 |
+
|
570 |
+
.shared-memory {
|
571 |
+
background: #000000;
|
572 |
+
color: #ffffff;
|
573 |
+
border: 2px solid #000000;
|
574 |
+
padding: 10px 20px;
|
575 |
+
font-size: 12px;
|
576 |
+
font-weight: bold;
|
577 |
+
text-align: center;
|
578 |
+
width: 80%;
|
579 |
+
}
|
580 |
+
|
581 |
+
.warp-flow {
|
582 |
+
display: flex;
|
583 |
+
gap: 30px;
|
584 |
+
align-items: flex-start;
|
585 |
+
margin: 20px 0;
|
586 |
+
}
|
587 |
+
|
588 |
+
.warp-scheduler {
|
589 |
+
background: #f8f8f8;
|
590 |
+
border: 2px solid #000000;
|
591 |
+
padding: 15px;
|
592 |
+
min-width: 200px;
|
593 |
+
}
|
594 |
+
|
595 |
+
.scheduler-title {
|
596 |
+
font-size: 14px;
|
597 |
+
font-weight: bold;
|
598 |
+
text-align: center;
|
599 |
+
margin-bottom: 15px;
|
600 |
+
background: #000000;
|
601 |
+
color: #ffffff;
|
602 |
+
padding: 8px;
|
603 |
+
}
|
604 |
+
|
605 |
+
.warp-queue {
|
606 |
+
display: flex;
|
607 |
+
flex-direction: column;
|
608 |
+
gap: 8px;
|
609 |
+
}
|
610 |
+
|
611 |
+
.warp-item {
|
612 |
+
padding: 8px 12px;
|
613 |
+
border: 1px solid #000000;
|
614 |
+
font-size: 11px;
|
615 |
+
font-weight: bold;
|
616 |
+
text-align: center;
|
617 |
+
}
|
618 |
+
|
619 |
+
.warp-item.active {
|
620 |
+
background: #000000;
|
621 |
+
color: #ffffff;
|
622 |
+
}
|
623 |
+
|
624 |
+
.warp-item.ready {
|
625 |
+
background: #d0d0d0;
|
626 |
+
}
|
627 |
+
|
628 |
+
.warp-item.waiting {
|
629 |
+
background: #f0f0f0;
|
630 |
+
}
|
631 |
+
|
632 |
+
.execution-pipeline {
|
633 |
+
flex: 1;
|
634 |
+
background: #f8f8f8;
|
635 |
+
border: 2px solid #000000;
|
636 |
+
padding: 15px;
|
637 |
+
}
|
638 |
+
|
639 |
+
.pipeline-step {
|
640 |
+
margin-bottom: 15px;
|
641 |
+
padding: 10px;
|
642 |
+
border: 1px solid #000000;
|
643 |
+
background: #ffffff;
|
644 |
+
}
|
645 |
+
|
646 |
+
.step-title {
|
647 |
+
font-size: 12px;
|
648 |
+
font-weight: bold;
|
649 |
+
margin-bottom: 5px;
|
650 |
+
color: #000000;
|
651 |
+
}
|
652 |
+
|
653 |
+
.step-content {
|
654 |
+
font-size: 11px;
|
655 |
+
color: #333333;
|
656 |
+
line-height: 1.4;
|
657 |
+
}
|
658 |
+
|
659 |
.current-view {
|
660 |
position: absolute;
|
661 |
top: 10px;
|
|
|
668 |
}
|
669 |
</style>
|
670 |
</head>
|
|
|
671 |
<body>
|
672 |
<div class="container">
|
673 |
<h1>Interactive 3D GPU Threading</h1>
|
674 |
+
|
675 |
<div class="controls">
|
676 |
<div class="control-group">
|
677 |
<label>Grid Dimensions (X × Y × Z)</label>
|
|
|
694 |
</div>
|
695 |
</div>
|
696 |
</div>
|
697 |
+
|
698 |
<div class="stats">
|
699 |
<div class="stat-box">
|
700 |
<div class="label">Total Blocks</div>
|
|
|
713 |
<div class="value" id="spatialDims">3×2×2</div>
|
714 |
</div>
|
715 |
</div>
|
716 |
+
|
717 |
<div class="interaction-guide">
|
718 |
<div class="interaction-item">
|
719 |
<span class="icon">🖱️</span>
|
|
|
732 |
<div>Double-Click to Reset View</div>
|
733 |
</div>
|
734 |
</div>
|
735 |
+
|
736 |
<div class="main-visualization">
|
737 |
<div class="cube-section">
|
738 |
<div class="section-title">Interactive 3D Block Grid</div>
|
739 |
+
|
740 |
<div class="cube-container" id="cubeContainer">
|
741 |
<div class="cube-3d" id="cube3d"></div>
|
742 |
<div class="current-view" id="currentView">
|
743 |
Rotation: X=15° Y=25° | Zoom: 1.0x
|
744 |
</div>
|
745 |
</div>
|
746 |
+
|
747 |
<div class="instructions">
|
748 |
+
Drag to rotate the cube and explore 3D spatial relationships • Scroll to zoom in/out • Click blocks to examine their threads • Double-click to reset view
|
|
|
749 |
</div>
|
750 |
</div>
|
751 |
+
|
752 |
<div class="info-panel">
|
753 |
<div class="info-title">Block & Thread Details</div>
|
754 |
<div id="blockInfo">
|
755 |
+
<div style="text-align: center; color: #666666; padding: 25px; border: 1px solid #cccccc; background: #f8f8f8;">
|
756 |
+
Rotate the cube to explore spatial relationships, then click on any block to see its thread organization and understand how neighboring blocks relate to each other in 3D space.
|
|
|
|
|
757 |
</div>
|
758 |
</div>
|
759 |
</div>
|
760 |
</div>
|
761 |
+
|
762 |
<div class="spatial-explanation">
|
763 |
<h3>Understanding Spatial Relationships Through Direct Manipulation</h3>
|
764 |
+
<p>The ability to directly manipulate the 3D cube with your cursor creates a much deeper understanding of spatial relationships than static views or sliders ever could. As you rotate the cube, you can see exactly which blocks are neighbors and how the grid extends in all three dimensions simultaneously.</p>
|
765 |
+
|
|
|
|
|
766 |
<p><strong>Why Direct Interaction Builds Better Mental Models:</strong></p>
|
767 |
+
<p>When you drag to rotate the cube, your brain naturally builds a spatial mental model that connects the visual structure to the mathematical relationships. You can see how a block at position [1,0,1] relates spatially to blocks at [0,0,1], [2,0,1], [1,1,1], and [1,0,0]. This spatial understanding is crucial for writing efficient GPU code that takes advantage of memory locality and thread cooperation patterns.</p>
|
768 |
+
|
769 |
+
<p><strong>Learning Exercise:</strong> Try rotating the cube to view it from different angles while thinking about how data flows through your algorithm. Can you identify which blocks would naturally work together on shared data? The spatial arrangement you see directly represents the memory access patterns that will determine your algorithm's performance.</p>
|
770 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
771 |
<div class="code-example">
|
772 |
+
// Example: 3D convolution filter processing
|
773 |
+
fn apply_3d_convolution(input: UnsafePointer[Float32], output: UnsafePointer[Float32],
|
774 |
+
width: Int, height: Int, depth: Int):
|
775 |
+
// Each thread processes one output voxel
|
776 |
+
let <span class="highlight">x = global_idx.x</span> // Width position
|
777 |
+
let <span class="highlight">y = global_idx.y</span> // Height position
|
778 |
+
let <span class="highlight">z = global_idx.z</span> // Depth position
|
779 |
+
|
780 |
+
if x < width and y < height and z < depth:
|
781 |
+
var sum: Float32 = 0
|
782 |
+
|
783 |
+
// Access neighboring voxels for convolution
|
784 |
+
for dz in range(-1, 2):
|
785 |
+
for dy in range(-1, 2):
|
786 |
+
for dx in range(-1, 2):
|
787 |
+
let nx = x + dx
|
788 |
+
let ny = y + dy
|
789 |
+
let nz = z + dz
|
790 |
+
|
791 |
+
if nx >= 0 and nx < width and ny >= 0 and ny < height and nz >= 0 and nz < depth:
|
792 |
+
let neighbor_idx = nz * width * height + ny * width + nx
|
793 |
+
sum += input[neighbor_idx] * filter_weights[dz+1][dy+1][dx+1]
|
794 |
+
|
795 |
+
let output_idx = z * width * height + y * width + x
|
796 |
+
output[output_idx] = sum
|
797 |
</div>
|
798 |
+
|
799 |
+
<p>In this convolution example, threads processing nearby voxels (which you can see as spatially close when you rotate the cube) will access overlapping sets of input data. The 3D spatial organization ensures that these threads are grouped into nearby blocks, enabling efficient sharing of cached memory containing the input voxels they all need to access.</p>
|
800 |
</div>
|
801 |
+
</div>
|
802 |
+
|
803 |
+
<script>
|
804 |
+
let selectedBlock = null;
|
805 |
+
let selectedThread = null;
|
806 |
+
let isMouseDown = false;
|
807 |
+
let lastMouseX = 0;
|
808 |
+
let lastMouseY = 0;
|
809 |
+
let rotationX = 15;
|
810 |
+
let rotationY = 25;
|
811 |
+
let zoom = 1.0;
|
812 |
+
|
813 |
+
function updateVisualization() {
|
814 |
+
const gridX = parseInt(document.getElementById('gridX').value);
|
815 |
+
const gridY = parseInt(document.getElementById('gridY').value);
|
816 |
+
const gridZ = parseInt(document.getElementById('gridZ').value);
|
817 |
+
const blockX = parseInt(document.getElementById('blockX').value);
|
818 |
+
const blockY = parseInt(document.getElementById('blockY').value);
|
819 |
+
const blockZ = parseInt(document.getElementById('blockZ').value);
|
820 |
+
|
821 |
+
const totalBlocks = gridX * gridY * gridZ;
|
822 |
+
const threadsPerBlock = blockX * blockY * blockZ;
|
823 |
+
const totalThreads = totalBlocks * threadsPerBlock;
|
824 |
+
|
825 |
+
// Update stats
|
826 |
+
document.getElementById('totalBlocks').textContent = totalBlocks;
|
827 |
+
document.getElementById('threadsPerBlock').textContent = threadsPerBlock;
|
828 |
+
document.getElementById('totalThreads').textContent = totalThreads;
|
829 |
+
document.getElementById('spatialDims').textContent = `${gridX}×${gridY}×${gridZ}`;
|
830 |
+
|
831 |
+
create3DCube();
|
832 |
+
selectedBlock = null;
|
833 |
+
selectedThread = null;
|
834 |
+
updateBlockInfo();
|
835 |
+
}
|
836 |
+
|
837 |
+
function create3DCube() {
|
838 |
+
const gridX = parseInt(document.getElementById('gridX').value);
|
839 |
+
const gridY = parseInt(document.getElementById('gridY').value);
|
840 |
+
const gridZ = parseInt(document.getElementById('gridZ').value);
|
841 |
+
const blockX = parseInt(document.getElementById('blockX').value);
|
842 |
+
const blockY = parseInt(document.getElementById('blockY').value);
|
843 |
+
const blockZ = parseInt(document.getElementById('blockZ').value);
|
844 |
+
|
845 |
+
const cube3d = document.getElementById('cube3d');
|
846 |
+
cube3d.innerHTML = '';
|
847 |
+
|
848 |
+
const spacing = 80; // Space between blocks
|
849 |
+
|
850 |
+
// Create all blocks in 3D space
|
851 |
+
for (let z = 0; z < gridZ; z++) {
|
852 |
+
for (let y = 0; y < gridY; y++) {
|
853 |
+
for (let x = 0; x < gridX; x++) {
|
854 |
+
const block = createBlock3D(x, y, z, blockX, blockY, blockZ, spacing);
|
855 |
+
cube3d.appendChild(block);
|
856 |
}
|
857 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
858 |
}
|
859 |
+
|
860 |
+
updateCubeTransform();
|
861 |
+
}
|
862 |
+
|
863 |
+
function createBlock3D(blockX_idx, blockY_idx, blockZ_idx, blockDimX, blockDimY, blockDimZ, spacing) {
|
864 |
+
const block = document.createElement('div');
|
865 |
+
block.className = 'block-cube';
|
866 |
+
|
867 |
+
// Calculate center offset for better visual balance
|
868 |
+
const gridX = parseInt(document.getElementById('gridX').value);
|
869 |
+
const gridY = parseInt(document.getElementById('gridY').value);
|
870 |
+
const gridZ = parseInt(document.getElementById('gridZ').value);
|
871 |
+
|
872 |
+
const centerOffsetX = (gridX - 1) * spacing / 2;
|
873 |
+
const centerOffsetY = (gridY - 1) * spacing / 2;
|
874 |
+
const centerOffsetZ = (gridZ - 1) * spacing / 2;
|
875 |
+
|
876 |
+
// Position in 3D space
|
877 |
+
const translateX = blockX_idx * spacing - centerOffsetX;
|
878 |
+
const translateY = blockY_idx * spacing - centerOffsetY;
|
879 |
+
const translateZ = blockZ_idx * spacing - centerOffsetZ;
|
880 |
+
|
881 |
+
block.style.transform = `translate3d(${translateX}px, ${translateY}px, ${translateZ}px)`;
|
882 |
+
|
883 |
+
// Block content
|
884 |
+
const label = document.createElement('div');
|
885 |
+
label.className = 'block-label';
|
886 |
+
label.textContent = `[${blockX_idx},${blockY_idx},${blockZ_idx}]`;
|
887 |
+
block.appendChild(label);
|
888 |
+
|
889 |
+
const threadCount = document.createElement('div');
|
890 |
+
threadCount.className = 'thread-count';
|
891 |
+
threadCount.textContent = `${blockDimX}×${blockDimY}×${blockDimZ}`;
|
892 |
+
block.appendChild(threadCount);
|
893 |
+
|
894 |
+
// Click handler
|
895 |
+
block.addEventListener('click', (e) => {
|
896 |
+
e.stopPropagation();
|
897 |
+
|
898 |
+
// Remove previous selection
|
899 |
+
document.querySelectorAll('.block-cube.selected').forEach(b => b.classList.remove('selected'));
|
900 |
+
|
901 |
+
// Select this block
|
902 |
+
block.classList.add('selected');
|
903 |
+
selectedBlock = {
|
904 |
+
idx: { x: blockX_idx, y: blockY_idx, z: blockZ_idx },
|
905 |
+
dim: { x: blockDimX, y: blockDimY, z: blockDimZ }
|
906 |
+
};
|
907 |
+
|
908 |
+
updateBlockInfo();
|
909 |
+
});
|
910 |
+
|
911 |
+
return block;
|
912 |
+
}
|
913 |
+
|
914 |
+
function updateCubeTransform() {
|
915 |
+
const cube3d = document.getElementById('cube3d');
|
916 |
+
cube3d.style.transform = `scale(${zoom}) rotateX(${rotationX}deg) rotateY(${rotationY}deg)`;
|
917 |
+
|
918 |
+
// Update current view display
|
919 |
+
document.getElementById('currentView').textContent =
|
920 |
+
`Rotation: X=${rotationX}° Y=${rotationY}° | Zoom: ${zoom.toFixed(1)}x`;
|
921 |
+
}
|
922 |
+
|
923 |
+
function updateBlockInfo() {
|
924 |
+
const blockInfo = document.getElementById('blockInfo');
|
925 |
+
|
926 |
+
if (!selectedBlock) {
|
927 |
+
blockInfo.innerHTML = `
|
928 |
<div style="text-align: center; color: #666666; padding: 25px; border: 1px solid #cccccc; background: #f8f8f8;">
|
929 |
Rotate the cube to explore spatial relationships, then click on any block to see its thread organization and understand how neighboring blocks relate to each other in 3D space.
|
930 |
</div>
|
931 |
`;
|
932 |
+
return;
|
933 |
+
}
|
934 |
+
|
935 |
+
const b = selectedBlock;
|
936 |
+
const threadsPerBlock = b.dim.x * b.dim.y * b.dim.z;
|
937 |
+
const warpSize = 32; // Standard warp size for modern GPUs
|
938 |
+
const numWarps = Math.ceil(threadsPerBlock / warpSize);
|
939 |
+
|
940 |
+
// Calculate spatial neighbors
|
941 |
+
const gridX = parseInt(document.getElementById('gridX').value);
|
942 |
+
const gridY = parseInt(document.getElementById('gridY').value);
|
943 |
+
const gridZ = parseInt(document.getElementById('gridZ').value);
|
944 |
+
|
945 |
+
const neighbors = [];
|
946 |
+
const directions = [
|
947 |
+
{ dx: -1, dy: 0, dz: 0, name: 'left' },
|
948 |
+
{ dx: 1, dy: 0, dz: 0, name: 'right' },
|
949 |
+
{ dx: 0, dy: -1, dz: 0, name: 'back' },
|
950 |
+
{ dx: 0, dy: 1, dz: 0, name: 'front' },
|
951 |
+
{ dx: 0, dy: 0, dz: -1, name: 'below' },
|
952 |
+
{ dx: 0, dy: 0, dz: 1, name: 'above' }
|
953 |
+
];
|
954 |
+
|
955 |
+
directions.forEach(dir => {
|
956 |
+
const nx = b.idx.x + dir.dx;
|
957 |
+
const ny = b.idx.y + dir.dy;
|
958 |
+
const nz = b.idx.z + dir.dz;
|
959 |
+
|
960 |
+
if (nx >= 0 && nx < gridX && ny >= 0 && ny < gridY && nz >= 0 && nz < gridZ) {
|
961 |
+
neighbors.push(`[${nx}, ${ny}, ${nz}] (${dir.name})`);
|
962 |
}
|
963 |
+
});
|
964 |
+
|
965 |
+
blockInfo.innerHTML = `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
966 |
<div class="info-item">
|
967 |
<div class="label">Selected Block</div>
|
968 |
<div class="value">Block [${b.idx.x}, ${b.idx.y}, ${b.idx.z}]</div>
|
|
|
974 |
</div>
|
975 |
|
976 |
<div class="info-item">
|
977 |
+
<div class="label">Warp Organization</div>
|
978 |
+
<div class="value">${numWarps} warp${numWarps > 1 ? 's' : ''} (${warpSize} threads each)</div>
|
979 |
+
</div>
|
980 |
+
|
981 |
+
<div style="margin-top: 20px; padding: 15px; background: #f0f0f0; border-left: 4px solid #000000;">
|
982 |
+
<div style="font-size: 12px; color: #333333; line-height: 1.6;">
|
983 |
+
<strong>SM Execution:</strong> When this block is assigned to a Streaming Multiprocessor (SM), it will be divided into ${numWarps} warp${numWarps > 1 ? 's' : ''}. ${numWarps === 1 ? 'All threads will execute in perfect synchronization.' : `The first ${numWarps - 1} warp${numWarps > 2 ? 's' : ''} will have ${warpSize} threads each, and the last warp will have ${threadsPerBlock % warpSize || warpSize} threads.`}
|
984 |
+
</div>
|
985 |
</div>
|
986 |
|
987 |
<div style="margin-top: 20px;">
|
|
|
999 |
|
1000 |
<div style="margin-top: 15px; padding: 15px; background: #e8e8e8; border: 1px solid #cccccc;">
|
1001 |
<div style="font-size: 11px; color: #333333;">
|
1002 |
+
💡 <strong>Performance Insight:</strong> Neighboring blocks (visible when you rotate the cube) are often assigned to nearby SMs, enabling efficient data sharing through the GPU's memory hierarchy. This spatial locality is crucial for achieving optimal memory bandwidth utilization.
|
1003 |
</div>
|
1004 |
</div>
|
1005 |
`;
|
1006 |
+
|
1007 |
+
createThreadGrid();
|
1008 |
+
}
|
1009 |
+
|
1010 |
+
function createWarpVisualization() {
|
1011 |
+
const warpDemo = document.getElementById('warpDemo');
|
1012 |
+
if (!warpDemo) return;
|
1013 |
+
|
1014 |
+
warpDemo.innerHTML = '';
|
1015 |
+
|
1016 |
+
// Create 32 threads representing one warp
|
1017 |
+
for (let i = 0; i < 32; i++) {
|
1018 |
+
const thread = document.createElement('div');
|
1019 |
+
thread.className = 'warp-thread warp-0';
|
1020 |
+
thread.textContent = i;
|
1021 |
+
thread.title = `Thread ${i} in Warp 0`;
|
1022 |
+
warpDemo.appendChild(thread);
|
1023 |
}
|
1024 |
+
}
|
1025 |
+
|
1026 |
+
function createThreadGrid() {
|
1027 |
+
if (!selectedBlock) return;
|
1028 |
+
|
1029 |
+
const threadGrid = document.getElementById('threadGrid');
|
1030 |
+
if (!threadGrid) return;
|
1031 |
+
|
1032 |
+
threadGrid.innerHTML = '';
|
1033 |
+
|
1034 |
+
const b = selectedBlock;
|
1035 |
+
const maxThreadsToShow = 64; // Limit for visual clarity
|
1036 |
+
const totalThreads = b.dim.x * b.dim.y * b.dim.z;
|
1037 |
+
|
1038 |
+
if (totalThreads > maxThreadsToShow) {
|
1039 |
+
threadGrid.innerHTML = `
|
1040 |
<div style="text-align: center; padding: 20px; color: #666666;">
|
1041 |
Too many threads to display (${totalThreads} total)<br>
|
1042 |
Try smaller block dimensions to see individual threads
|
1043 |
</div>
|
1044 |
`;
|
1045 |
+
return;
|
1046 |
+
}
|
1047 |
+
|
1048 |
+
threadGrid.style.gridTemplateColumns = `repeat(${Math.min(b.dim.x, 8)}, 1fr)`;
|
1049 |
+
|
1050 |
+
// Create threads for visualization
|
1051 |
+
for (let z = 0; z < Math.min(b.dim.z, 4); z++) { // Show max 4 Z layers
|
1052 |
+
for (let y = 0; y < b.dim.y; y++) {
|
1053 |
+
for (let x = 0; x < b.dim.x; x++) {
|
1054 |
+
const thread = document.createElement('div');
|
1055 |
+
thread.className = 'thread-detail';
|
1056 |
+
thread.textContent = `${x},${y},${z}`;
|
1057 |
+
thread.title = `Thread [${x}, ${y}, ${z}] in Block [${b.idx.x}, ${b.idx.y}, ${b.idx.z}]`;
|
1058 |
+
|
1059 |
+
// Calculate global position
|
1060 |
+
const globalX = b.dim.x * b.idx.x + x;
|
1061 |
+
const globalY = b.dim.y * b.idx.y + y;
|
1062 |
+
const globalZ = b.dim.z * b.idx.z + z;
|
1063 |
+
|
1064 |
+
thread.addEventListener('click', () => {
|
1065 |
+
document.querySelectorAll('.thread-detail.selected').forEach(t => t.classList.remove('selected'));
|
1066 |
+
thread.classList.add('selected');
|
1067 |
+
|
1068 |
+
selectedThread = {
|
1069 |
+
blockIdx: b.idx,
|
1070 |
+
threadIdx: { x, y, z },
|
1071 |
+
globalIdx: { x: globalX, y: globalY, z: globalZ },
|
1072 |
+
blockDim: b.dim
|
1073 |
+
};
|
1074 |
+
|
1075 |
+
showThreadDetails();
|
1076 |
+
});
|
1077 |
+
|
1078 |
+
threadGrid.appendChild(thread);
|
|
|
1079 |
}
|
1080 |
}
|
1081 |
}
|
1082 |
+
}
|
1083 |
+
|
1084 |
+
function showThreadDetails() {
|
1085 |
+
if (!selectedThread) return;
|
1086 |
+
|
1087 |
+
const t = selectedThread;
|
1088 |
+
const details = `
|
1089 |
<div style="margin-top: 20px; padding: 15px; background: #e8e8e8; border: 1px solid #000000;">
|
1090 |
<div style="font-size: 12px; font-weight: bold; margin-bottom: 10px;">Selected Thread Details:</div>
|
1091 |
<div style="font-size: 11px; line-height: 1.6;">
|
|
|
1099 |
</div>
|
1100 |
</div>
|
1101 |
`;
|
1102 |
+
|
1103 |
+
const blockInfo = document.getElementById('blockInfo');
|
1104 |
+
// Remove existing thread details
|
1105 |
+
const existing = blockInfo.querySelector('.thread-details');
|
1106 |
+
if (existing) existing.remove();
|
1107 |
+
|
1108 |
+
const detailsDiv = document.createElement('div');
|
1109 |
+
detailsDiv.className = 'thread-details';
|
1110 |
+
detailsDiv.innerHTML = details;
|
1111 |
+
blockInfo.appendChild(detailsDiv);
|
1112 |
+
}
|
1113 |
+
|
1114 |
+
// Mouse interaction setup
|
1115 |
+
function setupMouseInteraction() {
|
1116 |
+
const cubeContainer = document.getElementById('cubeContainer');
|
1117 |
+
|
1118 |
+
// Mouse down event
|
1119 |
+
cubeContainer.addEventListener('mousedown', (e) => {
|
1120 |
+
isMouseDown = true;
|
1121 |
+
lastMouseX = e.clientX;
|
1122 |
+
lastMouseY = e.clientY;
|
1123 |
+
cubeContainer.style.cursor = 'grabbing';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1124 |
});
|
1125 |
+
|
1126 |
+
// Mouse move event
|
1127 |
+
document.addEventListener('mousemove', (e) => {
|
1128 |
+
if (!isMouseDown) return;
|
1129 |
+
|
1130 |
+
const deltaX = e.clientX - lastMouseX;
|
1131 |
+
const deltaY = e.clientY - lastMouseY;
|
1132 |
+
|
1133 |
+
// Update rotation based on mouse movement
|
1134 |
+
rotationY += deltaX * 0.5; // Horizontal movement controls Y rotation
|
1135 |
+
rotationX -= deltaY * 0.5; // Vertical movement controls X rotation (inverted for natural feel)
|
1136 |
+
|
1137 |
+
// Constrain rotations to reasonable ranges
|
1138 |
+
rotationX = Math.max(-90, Math.min(90, rotationX));
|
1139 |
+
|
1140 |
+
updateCubeTransform();
|
1141 |
+
|
1142 |
+
lastMouseX = e.clientX;
|
1143 |
+
lastMouseY = e.clientY;
|
1144 |
+
});
|
1145 |
+
|
1146 |
+
// Mouse up event
|
1147 |
+
document.addEventListener('mouseup', () => {
|
1148 |
+
isMouseDown = false;
|
1149 |
+
cubeContainer.style.cursor = 'grab';
|
1150 |
+
});
|
1151 |
+
|
1152 |
+
// Mouse wheel event for zooming
|
1153 |
+
cubeContainer.addEventListener('wheel', (e) => {
|
1154 |
+
e.preventDefault();
|
1155 |
+
|
1156 |
+
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
1157 |
+
zoom *= zoomFactor;
|
1158 |
+
zoom = Math.max(0.3, Math.min(3.0, zoom)); // Constrain zoom range
|
1159 |
+
|
1160 |
+
updateCubeTransform();
|
1161 |
+
});
|
1162 |
+
|
1163 |
+
// Double-click to reset view
|
1164 |
+
cubeContainer.addEventListener('dblclick', () => {
|
1165 |
+
rotationX = 15;
|
1166 |
+
rotationY = 25;
|
1167 |
+
zoom = 1.0;
|
1168 |
+
updateCubeTransform();
|
1169 |
+
});
|
1170 |
+
|
1171 |
+
// Prevent context menu on right-click
|
1172 |
+
cubeContainer.addEventListener('contextmenu', (e) => {
|
1173 |
+
e.preventDefault();
|
1174 |
+
});
|
1175 |
+
}
|
1176 |
+
|
1177 |
+
// Event listeners for dimension controls
|
1178 |
+
['gridX', 'gridY', 'gridZ', 'blockX', 'blockY', 'blockZ'].forEach(id => {
|
1179 |
+
document.getElementById(id).addEventListener('input', updateVisualization);
|
1180 |
+
});
|
1181 |
+
|
1182 |
+
// Initialize everything
|
1183 |
+
setupMouseInteraction();
|
1184 |
+
updateVisualization();
|
1185 |
+
createWarpVisualization();
|
1186 |
+
</script>
|
1187 |
</body>
|
|
|
1188 |
</html>
|