|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Interactive 3D GPU Threading</title> |
|
<style> |
|
body { |
|
font-family: 'Consolas', 'Courier New', monospace; |
|
background: #ffffff; |
|
color: #000000; |
|
margin: 0; |
|
padding: 20px; |
|
line-height: 1.6; |
|
user-select: none; |
|
overflow-x: auto; |
|
} |
|
|
|
.container { |
|
max-width: 1400px; |
|
margin: 0 auto; |
|
background: #ffffff; |
|
border: 2px solid #000000; |
|
padding: clamp(15px, 4vw, 30px); |
|
box-sizing: border-box; |
|
} |
|
|
|
h1 { |
|
text-align: center; |
|
margin-bottom: clamp(20px, 5vw, 40px); |
|
color: #000000; |
|
font-weight: bold; |
|
font-size: clamp(18px, 4vw, 24px); |
|
border-bottom: 2px solid #000000; |
|
padding-bottom: 15px; |
|
} |
|
|
|
.controls { |
|
display: flex; |
|
gap: clamp(15px, 3vw, 30px); |
|
margin-bottom: clamp(20px, 5vw, 40px); |
|
flex-wrap: wrap; |
|
justify-content: center; |
|
align-items: center; |
|
width: 100%; |
|
box-sizing: border-box; |
|
} |
|
|
|
.control-group { |
|
background: #ffffff; |
|
padding: clamp(10px, 2vw, 20px); |
|
border: 1px solid #000000; |
|
min-width: 200px; |
|
flex: 1 1 auto; |
|
max-width: 300px; |
|
box-sizing: border-box; |
|
} |
|
|
|
|
|
@media (max-width: 767px) { |
|
.controls { |
|
flex-direction: column; |
|
gap: 15px; |
|
padding: 0; |
|
} |
|
|
|
.control-group { |
|
min-width: 0; |
|
width: 100%; |
|
max-width: 100%; |
|
flex: none; |
|
} |
|
|
|
.dimension-inputs { |
|
max-width: 100%; |
|
overflow: hidden; |
|
} |
|
} |
|
|
|
.control-group label { |
|
display: block; |
|
margin-bottom: 10px; |
|
font-weight: bold; |
|
color: #000000; |
|
text-align: center; |
|
font-size: clamp(12px, 2.5vw, 14px); |
|
} |
|
|
|
.dimension-inputs { |
|
display: flex; |
|
gap: 10px; |
|
align-items: center; |
|
justify-content: center; |
|
flex-wrap: wrap; |
|
} |
|
|
|
.dimension-inputs input { |
|
width: clamp(40px, 8vw, 50px); |
|
height: clamp(36px, 8vw, 44px); |
|
padding: 8px; |
|
border: 1px solid #000000; |
|
background: #ffffff; |
|
color: #000000; |
|
font-weight: bold; |
|
text-align: center; |
|
font-family: 'Consolas', 'Courier New', monospace; |
|
font-size: clamp(12px, 2.5vw, 14px); |
|
box-sizing: border-box; |
|
} |
|
|
|
|
|
@media (max-width: 767px) { |
|
.control-group { |
|
padding: 12px 15px; |
|
margin-bottom: 15px; |
|
} |
|
|
|
.control-group label { |
|
font-size: 13px; |
|
margin-bottom: 8px; |
|
} |
|
|
|
.dimension-inputs input { |
|
width: 45px; |
|
height: 40px; |
|
font-size: 14px; |
|
padding: 6px; |
|
} |
|
|
|
.dimension-inputs .label { |
|
font-size: 14px; |
|
} |
|
} |
|
|
|
.dimension-inputs .label { |
|
font-size: clamp(12px, 2.5vw, 14px); |
|
color: #000000; |
|
margin: 0 5px; |
|
flex-shrink: 0; |
|
} |
|
|
|
.stats { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); |
|
gap: clamp(10px, 2vw, 20px); |
|
margin-bottom: clamp(20px, 5vw, 40px); |
|
max-width: 800px; |
|
margin-left: auto; |
|
margin-right: auto; |
|
} |
|
|
|
.stat-box { |
|
background: #ffffff; |
|
padding: clamp(10px, 2vw, 20px); |
|
border: 1px solid #000000; |
|
text-align: center; |
|
min-width: 100px; |
|
} |
|
|
|
.stat-box .label { |
|
font-size: clamp(10px, 2vw, 12px); |
|
margin-bottom: 8px; |
|
color: #666666; |
|
text-transform: uppercase; |
|
letter-spacing: 1px; |
|
} |
|
|
|
.stat-box .value { |
|
font-size: clamp(18px, 4vw, 24px); |
|
font-weight: bold; |
|
color: #000000; |
|
} |
|
|
|
.main-visualization { |
|
display: flex; |
|
flex-direction: column; |
|
gap: clamp(20px, 4vw, 40px); |
|
margin-bottom: clamp(20px, 5vw, 40px); |
|
} |
|
|
|
|
|
@media (min-width: 1024px) { |
|
.main-visualization { |
|
flex-direction: row; |
|
} |
|
|
|
.cube-section { |
|
flex: 1; |
|
min-width: 500px; |
|
} |
|
|
|
.info-panel { |
|
flex: 0 0 350px; |
|
} |
|
} |
|
|
|
|
|
@media (min-width: 768px) and (max-width: 1023px) { |
|
.main-visualization { |
|
flex-direction: row; |
|
} |
|
|
|
.cube-section { |
|
flex: 1.2; |
|
min-width: 400px; |
|
} |
|
|
|
.info-panel { |
|
flex: 0.8; |
|
min-width: 280px; |
|
} |
|
} |
|
|
|
|
|
@media (max-width: 767px) { |
|
.main-visualization { |
|
flex-direction: column; |
|
} |
|
|
|
.cube-section { |
|
width: 100%; |
|
order: 1; |
|
} |
|
|
|
.info-panel { |
|
width: 100%; |
|
order: 2; |
|
max-height: none; |
|
} |
|
|
|
.controls { |
|
flex-direction: column; |
|
align-items: stretch; |
|
} |
|
|
|
.control-group { |
|
max-width: none; |
|
width: 100%; |
|
} |
|
} |
|
|
|
.cube-section { |
|
display: flex; |
|
flex-direction: column; |
|
} |
|
|
|
.section-title { |
|
text-align: center; |
|
margin-bottom: 20px; |
|
font-size: clamp(16px, 3vw, 18px); |
|
font-weight: bold; |
|
color: #000000; |
|
border-bottom: 1px solid #000000; |
|
padding-bottom: 10px; |
|
} |
|
|
|
.interaction-guide { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); |
|
gap: clamp(8px, 2vw, 15px); |
|
margin-bottom: 20px; |
|
padding: clamp(10px, 2vw, 15px); |
|
background: #f8f8f8; |
|
border: 1px solid #cccccc; |
|
font-size: clamp(10px, 2vw, 12px); |
|
} |
|
|
|
.interaction-item { |
|
text-align: center; |
|
color: #333333; |
|
padding: 5px; |
|
} |
|
|
|
.interaction-item .icon { |
|
font-size: clamp(14px, 3vw, 18px); |
|
margin-bottom: 5px; |
|
display: block; |
|
} |
|
|
|
.cube-container { |
|
height: clamp(300px, 50vw, 500px); |
|
width: 100%; |
|
perspective: 1200px; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
border: 1px solid #cccccc; |
|
background: #fafafa; |
|
margin-bottom: 20px; |
|
cursor: grab; |
|
overflow: hidden; |
|
position: relative; |
|
box-sizing: border-box; |
|
} |
|
|
|
.cube-container:active { |
|
cursor: grabbing; |
|
} |
|
|
|
.cube-3d { |
|
position: relative; |
|
transform-style: preserve-3d; |
|
transition: transform 0.1s ease-out; |
|
} |
|
|
|
.block-cube { |
|
position: absolute; |
|
width: clamp(50px, 8vw, 70px); |
|
height: clamp(50px, 8vw, 70px); |
|
background: #ffffff; |
|
border: 2px solid #000000; |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
justify-content: center; |
|
cursor: pointer; |
|
transition: all 0.2s ease; |
|
font-weight: bold; |
|
text-align: center; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
} |
|
|
|
.block-cube:hover { |
|
background: #f0f0f0; |
|
border-width: 3px; |
|
transform: scale(1.1); |
|
z-index: 10; |
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); |
|
} |
|
|
|
.block-cube.selected { |
|
background: #000000; |
|
color: #ffffff; |
|
border-width: 3px; |
|
z-index: 5; |
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); |
|
} |
|
|
|
.block-label { |
|
font-size: clamp(9px, 1.5vw, 11px); |
|
line-height: 1.2; |
|
margin-bottom: 6px; |
|
font-weight: bold; |
|
color: #000000; |
|
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); |
|
} |
|
|
|
.block-cube.selected .block-label { |
|
color: #ffffff; |
|
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8); |
|
} |
|
|
|
.thread-count { |
|
font-size: clamp(7px, 1.2vw, 9px); |
|
color: #333333; |
|
font-weight: bold; |
|
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); |
|
} |
|
|
|
.block-cube.selected .thread-count { |
|
color: #cccccc; |
|
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8); |
|
} |
|
|
|
.info-panel { |
|
background: #ffffff; |
|
border: 2px solid #000000; |
|
padding: clamp(15px, 3vw, 25px); |
|
max-height: 600px; |
|
overflow-y: auto; |
|
box-sizing: border-box; |
|
} |
|
|
|
@media (max-width: 767px) { |
|
.info-panel { |
|
max-height: 400px; |
|
} |
|
} |
|
|
|
.info-title { |
|
font-size: clamp(16px, 3vw, 18px); |
|
font-weight: bold; |
|
margin-bottom: 25px; |
|
text-align: center; |
|
color: #000000; |
|
border-bottom: 1px solid #000000; |
|
padding-bottom: 10px; |
|
} |
|
|
|
.info-item { |
|
margin-bottom: 20px; |
|
padding: clamp(10px, 2vw, 15px); |
|
background: #f8f8f8; |
|
border-left: 4px solid #000000; |
|
} |
|
|
|
.info-item .label { |
|
font-size: clamp(10px, 2vw, 12px); |
|
margin-bottom: 8px; |
|
color: #666666; |
|
text-transform: uppercase; |
|
letter-spacing: 1px; |
|
} |
|
|
|
.info-item .value { |
|
font-size: clamp(14px, 2.5vw, 16px); |
|
font-weight: bold; |
|
color: #000000; |
|
} |
|
|
|
.thread-detail-grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(clamp(20px, 4vw, 26px), 1fr)); |
|
gap: 1px; |
|
margin-top: 15px; |
|
padding: clamp(8px, 2vw, 12px); |
|
background: #f0f0f0; |
|
border: 1px solid #cccccc; |
|
} |
|
|
|
.thread-detail { |
|
width: clamp(20px, 4vw, 24px); |
|
height: clamp(20px, 4vw, 24px); |
|
background: #ffffff; |
|
border: 1px solid #000000; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: clamp(6px, 1.5vw, 8px); |
|
font-weight: bold; |
|
cursor: pointer; |
|
transition: all 0.2s ease; |
|
} |
|
|
|
.thread-detail:hover { |
|
background: #000000; |
|
color: #ffffff; |
|
transform: scale(1.2); |
|
} |
|
|
|
.thread-detail.selected { |
|
background: #000000; |
|
color: #ffffff; |
|
} |
|
|
|
.instructions { |
|
text-align: center; |
|
margin-top: 20px; |
|
font-size: clamp(12px, 2vw, 14px); |
|
color: #666666; |
|
border: 1px solid #cccccc; |
|
padding: clamp(10px, 2vw, 15px); |
|
background: #f8f8f8; |
|
line-height: 1.5; |
|
} |
|
|
|
.current-view { |
|
position: absolute; |
|
top: 10px; |
|
right: 10px; |
|
background: rgba(255, 255, 255, 0.9); |
|
padding: clamp(6px, 1.5vw, 8px) clamp(8px, 2vw, 12px); |
|
border: 1px solid #cccccc; |
|
font-size: clamp(9px, 1.5vw, 11px); |
|
color: #666666; |
|
} |
|
|
|
|
|
@media (pointer: coarse) { |
|
.thread-detail { |
|
min-width: 28px; |
|
min-height: 28px; |
|
} |
|
|
|
.dimension-inputs input { |
|
min-height: 44px; |
|
min-width: 50px; |
|
} |
|
|
|
.block-cube { |
|
min-width: 60px; |
|
min-height: 60px; |
|
} |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
<h1>Interactive 3D GPU Threading</h1> |
|
|
|
<div class="controls"> |
|
<div class="control-group"> |
|
<label>Grid Dimensions (X × Y × Z)</label> |
|
<div class="dimension-inputs"> |
|
<input type="number" id="gridX" value="3" min="1" max="4"> |
|
<span class="label">×</span> |
|
<input type="number" id="gridY" value="2" min="1" max="4"> |
|
<span class="label">×</span> |
|
<input type="number" id="gridZ" value="2" min="1" max="4"> |
|
</div> |
|
</div> |
|
<div class="control-group"> |
|
<label>Block Dimensions (X × Y × Z)</label> |
|
<div class="dimension-inputs"> |
|
<input type="number" id="blockX" value="2" min="1" max="4"> |
|
<span class="label">×</span> |
|
<input type="number" id="blockY" value="2" min="1" max="4"> |
|
<span class="label">×</span> |
|
<input type="number" id="blockZ" value="2" min="1" max="4"> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="stats"> |
|
<div class="stat-box"> |
|
<div class="label">Total Blocks</div> |
|
<div class="value" id="totalBlocks">12</div> |
|
</div> |
|
<div class="stat-box"> |
|
<div class="label">Threads per Block</div> |
|
<div class="value" id="threadsPerBlock">8</div> |
|
</div> |
|
<div class="stat-box"> |
|
<div class="label">Total Threads</div> |
|
<div class="value" id="totalThreads">96</div> |
|
</div> |
|
<div class="stat-box"> |
|
<div class="label">Spatial Dimensions</div> |
|
<div class="value" id="spatialDims">3×2×2</div> |
|
</div> |
|
</div> |
|
|
|
<div class="interaction-guide"> |
|
<div class="interaction-item"> |
|
<span class="icon">🖱️</span> |
|
<div>Drag to Rotate</div> |
|
</div> |
|
<div class="interaction-item"> |
|
<span class="icon">↕️</span> |
|
<div>Scroll to Zoom</div> |
|
</div> |
|
<div class="interaction-item"> |
|
<span class="icon">👆</span> |
|
<div>Click Blocks to Explore</div> |
|
</div> |
|
<div class="interaction-item"> |
|
<span class="icon">🔄</span> |
|
<div>Double-Click to Reset View</div> |
|
</div> |
|
</div> |
|
|
|
<div class="main-visualization"> |
|
<div class="cube-section"> |
|
<div class="section-title">Interactive 3D Block Grid</div> |
|
|
|
<div class="cube-container" id="cubeContainer"> |
|
<div class="cube-3d" id="cube3d"></div> |
|
<div class="current-view" id="currentView"> |
|
Rotation: X=15° Y=25° | Zoom: 1.0x |
|
</div> |
|
</div> |
|
|
|
<div class="instructions"> |
|
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 |
|
</div> |
|
</div> |
|
|
|
<div class="info-panel"> |
|
<div class="info-title">Block & Thread Details</div> |
|
<div id="blockInfo"> |
|
<div style="text-align: center; color: #666666; padding: 25px; border: 1px solid #cccccc; background: #f8f8f8;"> |
|
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. |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
let selectedBlock = null; |
|
let selectedThread = null; |
|
let isMouseDown = false; |
|
let lastMouseX = 0; |
|
let lastMouseY = 0; |
|
let rotationX = 15; |
|
let rotationY = 25; |
|
let zoom = 1.0; |
|
|
|
function updateVisualization() { |
|
const gridX = parseInt(document.getElementById('gridX').value); |
|
const gridY = parseInt(document.getElementById('gridY').value); |
|
const gridZ = parseInt(document.getElementById('gridZ').value); |
|
const blockX = parseInt(document.getElementById('blockX').value); |
|
const blockY = parseInt(document.getElementById('blockY').value); |
|
const blockZ = parseInt(document.getElementById('blockZ').value); |
|
|
|
const totalBlocks = gridX * gridY * gridZ; |
|
const threadsPerBlock = blockX * blockY * blockZ; |
|
const totalThreads = totalBlocks * threadsPerBlock; |
|
|
|
|
|
document.getElementById('totalBlocks').textContent = totalBlocks; |
|
document.getElementById('threadsPerBlock').textContent = threadsPerBlock; |
|
document.getElementById('totalThreads').textContent = totalThreads; |
|
document.getElementById('spatialDims').textContent = `${gridX}×${gridY}×${gridZ}`; |
|
|
|
|
|
let previousSelection = null; |
|
if (selectedBlock) { |
|
previousSelection = { |
|
x: selectedBlock.idx.x, |
|
y: selectedBlock.idx.y, |
|
z: selectedBlock.idx.z |
|
}; |
|
} |
|
|
|
create3DCube(); |
|
|
|
|
|
if (previousSelection && |
|
previousSelection.x < gridX && |
|
previousSelection.y < gridY && |
|
previousSelection.z < gridZ) { |
|
|
|
|
|
selectedBlock = { |
|
idx: { |
|
x: previousSelection.x, |
|
y: previousSelection.y, |
|
z: previousSelection.z |
|
}, |
|
dim: { |
|
x: blockX, |
|
y: blockY, |
|
z: blockZ |
|
} |
|
}; |
|
|
|
|
|
const blocks = document.querySelectorAll('.block-cube'); |
|
blocks.forEach(block => { |
|
const label = block.querySelector('.block-label'); |
|
if (label && label.textContent === `[${previousSelection.x},${previousSelection.y},${previousSelection.z}]`) { |
|
block.classList.add('selected'); |
|
} |
|
}); |
|
|
|
|
|
selectedThread = null; |
|
|
|
|
|
updateBlockInfo(); |
|
} else { |
|
|
|
selectedBlock = null; |
|
selectedThread = null; |
|
updateBlockInfo(); |
|
} |
|
} |
|
|
|
function create3DCube() { |
|
const gridX = parseInt(document.getElementById('gridX').value); |
|
const gridY = parseInt(document.getElementById('gridY').value); |
|
const gridZ = parseInt(document.getElementById('gridZ').value); |
|
const blockX = parseInt(document.getElementById('blockX').value); |
|
const blockY = parseInt(document.getElementById('blockY').value); |
|
const blockZ = parseInt(document.getElementById('blockZ').value); |
|
|
|
const cube3d = document.getElementById('cube3d'); |
|
cube3d.innerHTML = ''; |
|
|
|
const spacing = 80; |
|
|
|
|
|
const centerOffsetX = (gridX - 1) * spacing / 2; |
|
const centerOffsetY = (gridY - 1) * spacing / 2; |
|
const centerOffsetZ = (gridZ - 1) * spacing / 2; |
|
|
|
|
|
for (let z = 0; z < gridZ; z++) { |
|
for (let y = 0; y < gridY; y++) { |
|
for (let x = 0; x < gridX; x++) { |
|
const block = createBlock3D(x, y, z, blockX, blockY, blockZ, spacing, centerOffsetX, centerOffsetY, centerOffsetZ); |
|
cube3d.appendChild(block); |
|
} |
|
} |
|
} |
|
|
|
updateCubeTransform(); |
|
} |
|
|
|
function createBlock3D(blockX_idx, blockY_idx, blockZ_idx, blockDimX, blockDimY, blockDimZ, spacing, centerOffsetX, centerOffsetY, centerOffsetZ) { |
|
const block = document.createElement('div'); |
|
block.className = 'block-cube'; |
|
|
|
|
|
const translateX = blockX_idx * spacing - centerOffsetX; |
|
const translateY = blockY_idx * spacing - centerOffsetY; |
|
const translateZ = -(blockZ_idx * spacing - centerOffsetZ); |
|
|
|
block.style.transform = `translate3d(${translateX}px, ${translateY}px, ${translateZ}px)`; |
|
|
|
|
|
const label = document.createElement('div'); |
|
label.className = 'block-label'; |
|
label.textContent = `[${blockX_idx},${blockY_idx},${blockZ_idx}]`; |
|
block.appendChild(label); |
|
|
|
const threadCount = document.createElement('div'); |
|
threadCount.className = 'thread-count'; |
|
threadCount.textContent = `${blockDimX}×${blockDimY}×${blockDimZ}`; |
|
block.appendChild(threadCount); |
|
|
|
|
|
block.addEventListener('click', (e) => { |
|
e.stopPropagation(); |
|
|
|
|
|
document.querySelectorAll('.block-cube.selected').forEach(b => b.classList.remove('selected')); |
|
|
|
|
|
block.classList.add('selected'); |
|
selectedBlock = { |
|
idx: { x: blockX_idx, y: blockY_idx, z: blockZ_idx }, |
|
dim: { x: blockDimX, y: blockDimY, z: blockDimZ } |
|
}; |
|
|
|
updateBlockInfo(); |
|
}); |
|
|
|
return block; |
|
} |
|
|
|
function updateCubeTransform() { |
|
const cube3d = document.getElementById('cube3d'); |
|
cube3d.style.transform = `scale(${zoom}) rotateX(${rotationX}deg) rotateY(${rotationY}deg)`; |
|
|
|
|
|
document.getElementById('currentView').textContent = |
|
`Rotation: X=${rotationX}° Y=${rotationY}° | Zoom: ${zoom.toFixed(1)}x`; |
|
} |
|
|
|
function updateBlockInfo() { |
|
const blockInfo = document.getElementById('blockInfo'); |
|
|
|
if (!selectedBlock) { |
|
blockInfo.innerHTML = ` |
|
<div style="text-align: center; color: #666666; padding: 25px; border: 1px solid #cccccc; background: #f8f8f8;"> |
|
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. |
|
</div> |
|
`; |
|
return; |
|
} |
|
|
|
|
|
const currentBlockX = parseInt(document.getElementById('blockX').value); |
|
const currentBlockY = parseInt(document.getElementById('blockY').value); |
|
const currentBlockZ = parseInt(document.getElementById('blockZ').value); |
|
|
|
|
|
selectedBlock.dim = { |
|
x: currentBlockX, |
|
y: currentBlockY, |
|
z: currentBlockZ |
|
}; |
|
|
|
const b = selectedBlock; |
|
const threadsPerBlock = currentBlockX * currentBlockY * currentBlockZ; |
|
const warpSize = 32; |
|
const numWarps = Math.ceil(threadsPerBlock / warpSize); |
|
|
|
|
|
const gridX = parseInt(document.getElementById('gridX').value); |
|
const gridY = parseInt(document.getElementById('gridY').value); |
|
const gridZ = parseInt(document.getElementById('gridZ').value); |
|
|
|
const neighbors = []; |
|
const directions = [ |
|
{ dx: -1, dy: 0, dz: 0, name: 'left' }, |
|
{ dx: 1, dy: 0, dz: 0, name: 'right' }, |
|
{ dx: 0, dy: -1, dz: 0, name: 'top' }, |
|
{ dx: 0, dy: 1, dz: 0, name: 'bottom' }, |
|
{ dx: 0, dy: 0, dz: -1, name: 'front' }, |
|
{ dx: 0, dy: 0, dz: 1, name: 'back' } |
|
]; |
|
|
|
directions.forEach(dir => { |
|
const nx = b.idx.x + dir.dx; |
|
const ny = b.idx.y + dir.dy; |
|
const nz = b.idx.z + dir.dz; |
|
|
|
if (nx >= 0 && nx < gridX && ny >= 0 && ny < gridY && nz >= 0 && nz < gridZ) { |
|
neighbors.push(`[${nx}, ${ny}, ${nz}] (${dir.name})`); |
|
} |
|
}); |
|
|
|
blockInfo.innerHTML = ` |
|
<div class="info-item"> |
|
<div class="label">Selected Block</div> |
|
<div class="value">Block [${b.idx.x}, ${b.idx.y}, ${b.idx.z}]</div> |
|
</div> |
|
|
|
<div class="info-item"> |
|
<div class="label">Block Dimensions</div> |
|
<div class="value">[${currentBlockX}, ${currentBlockY}, ${currentBlockZ}] = ${threadsPerBlock} threads</div> |
|
</div> |
|
|
|
<div class="info-item"> |
|
<div class="label">Warp Organization</div> |
|
<div class="value">${numWarps} warp${numWarps > 1 ? 's' : ''} (${warpSize} threads each)</div> |
|
</div> |
|
|
|
<div style="margin-top: 20px; padding: 15px; background: #f0f0f0; border-left: 4px solid #000000;"> |
|
<div style="font-size: 12px; color: #333333; line-height: 1.6;"> |
|
<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.`} |
|
</div> |
|
</div> |
|
|
|
<div style="margin-top: 20px;"> |
|
<div style="font-size: 14px; font-weight: bold; margin-bottom: 15px; border-bottom: 1px solid #cccccc; padding-bottom: 8px;">Threads in This Block:</div> |
|
<div class="thread-detail-grid" id="threadGrid"></div> |
|
</div> |
|
|
|
<div style="margin-top: 25px; padding: 20px; background: #f0f0f0; border-left: 4px solid #000000;"> |
|
<div style="font-size: 12px; color: #333333; line-height: 1.6;"> |
|
<strong>Spatial Neighbors:</strong><br> |
|
This block's immediate neighbors in 3D space:<br> |
|
${neighbors.length > 0 ? neighbors.map(n => `• ${n}`).join('<br>') : '• No neighbors (edge/corner block)'} |
|
</div> |
|
</div> |
|
|
|
<div style="margin-top: 15px; padding: 15px; background: #e8e8e8; border: 1px solid #cccccc;"> |
|
<div style="font-size: 11px; color: #333333;"> |
|
💡 <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. |
|
</div> |
|
</div> |
|
`; |
|
|
|
createThreadGrid(); |
|
} |
|
|
|
function createThreadGrid() { |
|
if (!selectedBlock) return; |
|
|
|
const threadGrid = document.getElementById('threadGrid'); |
|
if (!threadGrid) return; |
|
|
|
threadGrid.innerHTML = ''; |
|
|
|
|
|
const currentBlockX = parseInt(document.getElementById('blockX').value); |
|
const currentBlockY = parseInt(document.getElementById('blockY').value); |
|
const currentBlockZ = parseInt(document.getElementById('blockZ').value); |
|
|
|
|
|
selectedBlock.dim = { |
|
x: currentBlockX, |
|
y: currentBlockY, |
|
z: currentBlockZ |
|
}; |
|
|
|
const maxThreadsToShow = 64; |
|
const totalThreads = currentBlockX * currentBlockY * currentBlockZ; |
|
|
|
if (totalThreads > maxThreadsToShow) { |
|
threadGrid.innerHTML = ` |
|
<div style="text-align: center; padding: 20px; color: #666666;"> |
|
Too many threads to display (${totalThreads} total)<br> |
|
Try smaller block dimensions to see individual threads |
|
</div> |
|
`; |
|
return; |
|
} |
|
|
|
threadGrid.style.gridTemplateColumns = `repeat(${Math.min(currentBlockX, 8)}, 1fr)`; |
|
|
|
|
|
for (let z = 0; z < Math.min(currentBlockZ, 4); z++) { |
|
for (let y = 0; y < currentBlockY; y++) { |
|
for (let x = 0; x < currentBlockX; x++) { |
|
const thread = document.createElement('div'); |
|
thread.className = 'thread-detail'; |
|
thread.textContent = `${x},${y},${z}`; |
|
thread.title = `Thread [${x}, ${y}, ${z}] in Block [${selectedBlock.idx.x}, ${selectedBlock.idx.y}, ${selectedBlock.idx.z}]`; |
|
|
|
|
|
const globalX = currentBlockX * selectedBlock.idx.x + x; |
|
const globalY = currentBlockY * selectedBlock.idx.y + y; |
|
const globalZ = currentBlockZ * selectedBlock.idx.z + z; |
|
|
|
thread.addEventListener('click', () => { |
|
document.querySelectorAll('.thread-detail.selected').forEach(t => t.classList.remove('selected')); |
|
thread.classList.add('selected'); |
|
|
|
selectedThread = { |
|
blockIdx: selectedBlock.idx, |
|
threadIdx: { x, y, z }, |
|
globalIdx: { x: globalX, y: globalY, z: globalZ }, |
|
blockDim: { x: currentBlockX, y: currentBlockY, z: currentBlockZ } |
|
}; |
|
|
|
showThreadDetails(); |
|
}); |
|
|
|
threadGrid.appendChild(thread); |
|
} |
|
} |
|
} |
|
} |
|
|
|
function showThreadDetails() { |
|
if (!selectedThread) return; |
|
|
|
const t = selectedThread; |
|
const details = ` |
|
<div style="margin-top: 20px; padding: 15px; background: #e8e8e8; border: 1px solid #000000;"> |
|
<div style="font-size: 12px; font-weight: bold; margin-bottom: 10px;">Selected Thread Details:</div> |
|
<div style="font-size: 11px; line-height: 1.6;"> |
|
<strong>Thread Index:</strong> [${t.threadIdx.x}, ${t.threadIdx.y}, ${t.threadIdx.z}]<br> |
|
<strong>Block Index:</strong> [${t.blockIdx.x}, ${t.blockIdx.y}, ${t.blockIdx.z}]<br> |
|
<strong>Global Index:</strong> [${t.globalIdx.x}, ${t.globalIdx.y}, ${t.globalIdx.z}]<br><br> |
|
<strong>Calculation:</strong><br> |
|
global_x = ${t.blockDim.x} × ${t.blockIdx.x} + ${t.threadIdx.x} = ${t.globalIdx.x}<br> |
|
global_y = ${t.blockDim.y} × ${t.blockIdx.y} + ${t.threadIdx.y} = ${t.globalIdx.y}<br> |
|
global_z = ${t.blockDim.z} × ${t.blockIdx.z} + ${t.threadIdx.z} = ${t.globalIdx.z} |
|
</div> |
|
</div> |
|
`; |
|
|
|
const blockInfo = document.getElementById('blockInfo'); |
|
|
|
const existing = blockInfo.querySelector('.thread-details'); |
|
if (existing) existing.remove(); |
|
|
|
const detailsDiv = document.createElement('div'); |
|
detailsDiv.className = 'thread-details'; |
|
detailsDiv.innerHTML = details; |
|
blockInfo.appendChild(detailsDiv); |
|
} |
|
|
|
|
|
function setupMouseInteraction() { |
|
const cubeContainer = document.getElementById('cubeContainer'); |
|
|
|
|
|
cubeContainer.addEventListener('mousedown', (e) => { |
|
isMouseDown = true; |
|
lastMouseX = e.clientX; |
|
lastMouseY = e.clientY; |
|
cubeContainer.style.cursor = 'grabbing'; |
|
}); |
|
|
|
|
|
document.addEventListener('mousemove', (e) => { |
|
if (!isMouseDown) return; |
|
|
|
const deltaX = e.clientX - lastMouseX; |
|
const deltaY = e.clientY - lastMouseY; |
|
|
|
|
|
rotationY += deltaX * 0.5; |
|
rotationX -= deltaY * 0.5; |
|
|
|
|
|
rotationX = Math.max(-90, Math.min(90, rotationX)); |
|
|
|
updateCubeTransform(); |
|
|
|
lastMouseX = e.clientX; |
|
lastMouseY = e.clientY; |
|
}); |
|
|
|
|
|
document.addEventListener('mouseup', () => { |
|
isMouseDown = false; |
|
cubeContainer.style.cursor = 'grab'; |
|
}); |
|
|
|
|
|
cubeContainer.addEventListener('wheel', (e) => { |
|
e.preventDefault(); |
|
|
|
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1; |
|
zoom *= zoomFactor; |
|
zoom = Math.max(0.3, Math.min(3.0, zoom)); |
|
|
|
updateCubeTransform(); |
|
}); |
|
|
|
|
|
cubeContainer.addEventListener('dblclick', () => { |
|
rotationX = 15; |
|
rotationY = 25; |
|
zoom = 1.0; |
|
updateCubeTransform(); |
|
}); |
|
|
|
|
|
cubeContainer.addEventListener('contextmenu', (e) => { |
|
e.preventDefault(); |
|
}); |
|
} |
|
|
|
|
|
['gridX', 'gridY', 'gridZ', 'blockX', 'blockY', 'blockZ'].forEach(id => { |
|
document.getElementById(id).addEventListener('input', updateVisualization); |
|
}); |
|
|
|
|
|
setupMouseInteraction(); |
|
updateVisualization(); |
|
</script> |
|
</body> |
|
</html> |