From 6816b976f5ba60b57fb3c6c67757161bdc711a34 Mon Sep 17 00:00:00 2001 From: jrprice Date: Tue, 4 Jun 2024 21:32:36 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20=20@=202678a?= =?UTF-8?q?b34f2d538611bf4228d9f524642e09dfe37=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/nbody.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/nbody.js b/dist/nbody.js index 21e3aa4..9b2f854 100644 --- a/dist/nbody.js +++ b/dist/nbody.js @@ -46,7 +46,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"// Simulation parameters.\\nconst kDelta = 0.000025;\\nconst kSoftening = 0.2;\\n\\n@group(0) @binding(0)\\nvar positionsIn : array>;\\n\\n@group(0) @binding(1)\\nvar positionsOut : array>;\\n\\n@group(0) @binding(2)\\nvar velocities : array>;\\n\\nfn computeForce(ipos : vec4,\\n jpos : vec4,\\n ) -> vec4 {\\n let d = vec4((jpos - ipos).xyz, 0);\\n let distSq = d.x*d.x + d.y*d.y + d.z*d.z + kSoftening*kSoftening;\\n let dist = inverseSqrt(distSq);\\n let coeff = jpos.w * (dist*dist*dist);\\n return coeff * d;\\n}\\n\\n@group(1) @binding(0) var image_a: texture_storage_2d;\\n@group(1) @binding(1) var image_b: texture_storage_2d;\\n\\n@compute @workgroup_size(kWorkgroupSize)\\nfn cs_main(\\n @builtin(global_invocation_id) gid : vec3,\\n ) {\\n let idx = gid.x;\\n let pos = positionsIn[idx];\\n\\n // Compute force.\\n var force = vec4(0.0);\\n for (var i = 0; i < kNumBodies; i++) {\\n force = force + computeForce(pos, positionsIn[i]);\\n }\\n\\n // Update velocity.\\n var velocity = velocities[idx];\\n velocity = velocity + force * kDelta;\\n velocities[idx] = velocity;\\n\\n textureStore(image_a, vec2(), vec4(1u));\\n textureStore(image_b, vec2(), vec4(2u));\\n\\n // Update position.\\n positionsOut[idx] = pos + velocity * kDelta;\\n}\\n\\nstruct RenderParams {\\n viewProjectionMatrix : mat4x4\\n}\\n\\n@group(0) @binding(0)\\nvar renderParams : RenderParams;\\n\\nstruct VertexOut {\\n @builtin(position) position : vec4,\\n @location(0) positionInQuad : vec2,\\n @location(1) @interpolate(flat) color : vec3,\\n}\\n\\n@vertex\\nfn vs_main(\\n @builtin(instance_index) idx : u32,\\n @builtin(vertex_index) vertex : u32,\\n @location(0) position : vec4,\\n ) -> VertexOut {\\n\\n let kPointRadius = 0.005;\\n let vertexOffsets = array, 6>(\\n vec2( 1.0, -1.0),\\n vec2(-1.0, -1.0),\\n vec2(-1.0, 1.0),\\n vec2(-1.0, 1.0),\\n vec2( 1.0, 1.0),\\n vec2( 1.0, -1.0),\\n );\\n let offset = vertexOffsets[vertex];\\n\\n var out : VertexOut;\\n out.position = renderParams.viewProjectionMatrix *\\n vec4(position.xy + offset * kPointRadius, position.zw);\\n out.positionInQuad = offset;\\n if (idx % 2 == 0) {\\n out.color = vec3(0.4, 0.4, 1.0);\\n } else {\\n out.color = vec3(1.0, 0.4, 0.4);\\n }\\n return out;\\n}\\n\\n@fragment\\nfn fs_main(\\n @builtin(position) position : vec4,\\n @location(0) positionInQuad : vec2,\\n @location(1) @interpolate(flat) color : vec3,\\n ) -> @location(0) vec4 {\\n // Calculate the normalized distance from this fragment to the quad center.\\n let distFromCenter = length(positionInQuad);\\n\\n // Discard fragments that are outside the circle.\\n if (distFromCenter > 1) {\\n discard;\\n }\\n\\n let intensity = 1 - distFromCenter;\\n return vec4(intensity*color, 1);\\n}\\n\");\n\n//# sourceURL=webpack://nbody-webgpu/./src/shaders.wgsl?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (\"// Simulation parameters.\\nconst kDelta = 0.000025;\\nconst kSoftening = 0.2;\\n\\n@group(0) @binding(0)\\nvar positionsIn : array>;\\n\\n@group(0) @binding(1)\\nvar positionsOut : array>;\\n\\n@group(0) @binding(2)\\nvar velocities : array>;\\n\\nfn computeForce(ipos : vec4,\\n jpos : vec4,\\n ) -> vec4 {\\n let d = vec4((jpos - ipos).xyz, 0);\\n let distSq = d.x*d.x + d.y*d.y + d.z*d.z + kSoftening*kSoftening;\\n let dist = inverseSqrt(distSq);\\n let coeff = jpos.w * (dist*dist*dist);\\n return coeff * d;\\n}\\n\\n@compute @workgroup_size(kWorkgroupSize)\\nfn cs_main(\\n @builtin(global_invocation_id) gid : vec3,\\n ) {\\n let idx = gid.x;\\n let pos = positionsIn[idx];\\n\\n // Compute force.\\n var force = vec4(0.0);\\n for (var i = 0; i < kNumBodies; i++) {\\n force = force + computeForce(pos, positionsIn[i]);\\n }\\n\\n // Update velocity.\\n var velocity = velocities[idx];\\n velocity = velocity + force * kDelta;\\n velocities[idx] = velocity;\\n\\n // Update position.\\n positionsOut[idx] = pos + velocity * kDelta;\\n}\\n\\nstruct RenderParams {\\n viewProjectionMatrix : mat4x4\\n}\\n\\n@group(0) @binding(0)\\nvar renderParams : RenderParams;\\n\\nstruct VertexOut {\\n @builtin(position) position : vec4,\\n @location(0) positionInQuad : vec2,\\n @location(1) @interpolate(flat) color : vec3,\\n}\\n\\n@vertex\\nfn vs_main(\\n @builtin(instance_index) idx : u32,\\n @builtin(vertex_index) vertex : u32,\\n @location(0) position : vec4,\\n ) -> VertexOut {\\n\\n let kPointRadius = 0.005;\\n let vertexOffsets = array, 6>(\\n vec2( 1.0, -1.0),\\n vec2(-1.0, -1.0),\\n vec2(-1.0, 1.0),\\n vec2(-1.0, 1.0),\\n vec2( 1.0, 1.0),\\n vec2( 1.0, -1.0),\\n );\\n let offset = vertexOffsets[vertex];\\n\\n var out : VertexOut;\\n out.position = renderParams.viewProjectionMatrix *\\n vec4(position.xy + offset * kPointRadius, position.zw);\\n out.positionInQuad = offset;\\n if (idx % 2 == 0) {\\n out.color = vec3(0.4, 0.4, 1.0);\\n } else {\\n out.color = vec3(1.0, 0.4, 0.4);\\n }\\n return out;\\n}\\n\\n@fragment\\nfn fs_main(\\n @builtin(position) position : vec4,\\n @location(0) positionInQuad : vec2,\\n @location(1) @interpolate(flat) color : vec3,\\n ) -> @location(0) vec4 {\\n // Calculate the normalized distance from this fragment to the quad center.\\n let distFromCenter = length(positionInQuad);\\n\\n // Discard fragments that are outside the circle.\\n if (distFromCenter > 1) {\\n discard;\\n }\\n\\n let intensity = 1 - distFromCenter;\\n return vec4(intensity*color, 1);\\n}\\n\");\n\n//# sourceURL=webpack://nbody-webgpu/./src/shaders.wgsl?"); /***/ }), @@ -56,7 +56,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \**********************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var gl_matrix__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! gl-matrix */ \"./node_modules/gl-matrix/esm/mat4.js\");\n/* harmony import */ var gl_matrix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! gl-matrix */ \"./node_modules/gl-matrix/esm/vec3.js\");\n/* harmony import */ var _shaders_wgsl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./shaders.wgsl */ \"./src/shaders.wgsl\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Simulation parameters.\nlet numBodies;\n// Shader parameters.\nlet workgroupSize;\n// Controls.\nlet paused = false;\nlet currentKey = null;\n// Render parameters.\nlet eyePosition;\n// WebGPU objects.\nlet device = null;\nlet queue = null;\nlet computePipeline = null;\nlet renderPipeline = null;\nlet canvas = null;\nlet canvasContext = null;\nlet positionsIn = null;\nlet positionsOut = null;\nlet velocities = null;\nlet renderParams = null;\nlet computeBindGroup = null;\nlet othergroup = null;\nlet renderBindGroup = null;\nconst init = () => __awaiter(void 0, void 0, void 0, function* () {\n // Initialize the WebGPU device.\n const powerPref = document.getElementById(\"powerpref\");\n const adapter = yield navigator.gpu.requestAdapter({\n powerPreference: powerPref.selectedOptions[0].value,\n });\n device = yield adapter.requestDevice();\n queue = device.queue;\n // Set up the canvas context.\n canvas = document.getElementById(\"canvas\");\n canvasContext = canvas.getContext(\"webgpu\");\n});\n// Generate WGSL shader source.\nfunction getShaders() {\n let preamble = \"\";\n preamble += `const kWorkgroupSize = ${workgroupSize};\\n`;\n preamble += `const kNumBodies = ${numBodies};\\n`;\n return preamble + _shaders_wgsl__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n}\n// Get the selected number from a drop-down menu.\nfunction getSelectedNumber(id) {\n let list = document.getElementById(id);\n return Number(list.selectedOptions[0].value);\n}\nconst updateRenderParams = () => __awaiter(void 0, void 0, void 0, function* () {\n // Fit the canvas to the window.\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n canvasContext.configure({\n device: device,\n format: navigator.gpu.getPreferredCanvasFormat(),\n usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,\n });\n // Generate the view projection matrix.\n let projectionMatrix = gl_matrix__WEBPACK_IMPORTED_MODULE_1__.create();\n let viewProjectionMatrix = gl_matrix__WEBPACK_IMPORTED_MODULE_1__.create();\n gl_matrix__WEBPACK_IMPORTED_MODULE_1__.perspectiveZO(projectionMatrix, 1.0, canvas.width / canvas.height, 0.1, 50.0);\n gl_matrix__WEBPACK_IMPORTED_MODULE_1__.translate(viewProjectionMatrix, viewProjectionMatrix, eyePosition);\n gl_matrix__WEBPACK_IMPORTED_MODULE_1__.multiply(viewProjectionMatrix, projectionMatrix, viewProjectionMatrix);\n // Write the render parameters to the uniform buffer.\n let renderParamsHost = new ArrayBuffer(4 * 4 * 4);\n let viewProjectionMatrixHost = new Float32Array(renderParamsHost);\n viewProjectionMatrixHost.set(viewProjectionMatrix);\n queue.writeBuffer(renderParams, 0, renderParamsHost);\n});\nfunction initPipelines() {\n // Reset pipelines.\n renderPipeline = null;\n computePipeline = null;\n // Update pipeline options.\n workgroupSize = getSelectedNumber(\"wgsize\");\n // Create a uniform buffer for the render parameters.\n renderParams = device.createBuffer({\n size: 4 * 4 * 4,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n mappedAtCreation: false,\n });\n updateRenderParams();\n // Create the shader module.\n const module = device.createShaderModule({ code: getShaders() });\n // Create the render pipeline.\n const positionsAttribute = {\n shaderLocation: 0,\n offset: 0,\n format: \"float32x4\",\n };\n const positionsLayout = {\n attributes: [positionsAttribute],\n arrayStride: 4 * 4,\n stepMode: \"instance\",\n };\n renderPipeline = device.createRenderPipeline({\n vertex: {\n module: module,\n entryPoint: \"vs_main\",\n buffers: [positionsLayout],\n },\n fragment: {\n module: module,\n entryPoint: \"fs_main\",\n targets: [\n {\n format: navigator.gpu.getPreferredCanvasFormat(),\n blend: {\n color: {\n operation: \"add\",\n srcFactor: \"one\",\n dstFactor: \"one\",\n },\n alpha: {\n operation: \"add\",\n srcFactor: \"one\",\n dstFactor: \"one\",\n },\n },\n },\n ],\n },\n primitive: {\n frontFace: \"cw\",\n cullMode: \"none\",\n topology: \"triangle-list\",\n },\n layout: \"auto\",\n });\n // Create the compute pipeline.\n computePipeline = device.createComputePipeline({\n compute: {\n module: module,\n entryPoint: \"cs_main\",\n },\n layout: \"auto\",\n });\n}\nfunction initBodies() {\n // Create buffers for body positions and velocities.\n positionsIn = device.createBuffer({\n size: numBodies * 4 * 4,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX,\n mappedAtCreation: true,\n });\n positionsOut = device.createBuffer({\n size: numBodies * 4 * 4,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX,\n mappedAtCreation: false,\n });\n velocities = device.createBuffer({\n size: numBodies * 4 * 4,\n usage: GPUBufferUsage.STORAGE,\n mappedAtCreation: false,\n });\n // Generate initial positions on the surface of a sphere.\n const kRadius = 0.6;\n let positions = new Float32Array(positionsIn.getMappedRange());\n for (let i = 0; i < numBodies; i++) {\n let longitude = 2.0 * Math.PI * Math.random();\n let latitude = Math.acos(2.0 * Math.random() - 1.0);\n positions[i * 4 + 0] = kRadius * Math.sin(latitude) * Math.cos(longitude);\n positions[i * 4 + 1] = kRadius * Math.sin(latitude) * Math.sin(longitude);\n positions[i * 4 + 2] = kRadius * Math.cos(latitude);\n positions[i * 4 + 3] = 1.0;\n }\n positionsIn.unmap();\n}\n// Render loop.\nconst kFpsUpdateInterval = 500;\nlet numFramesSinceFpsUpdate = 0;\nlet lastFpsUpdateTime = null;\nfunction draw() {\n if (!computePipeline) {\n // Not ready yet.\n requestAnimationFrame(draw);\n return;\n }\n // Update the FPS counter.\n if (lastFpsUpdateTime) {\n const now = performance.now();\n const timeSinceLastLog = now - lastFpsUpdateTime;\n if (timeSinceLastLog >= kFpsUpdateInterval) {\n const fps = numFramesSinceFpsUpdate / (timeSinceLastLog / 1000.0);\n document.getElementById(\"fps\").innerHTML = fps.toFixed(1) + \" FPS\";\n lastFpsUpdateTime = performance.now();\n numFramesSinceFpsUpdate = 0;\n }\n }\n else {\n lastFpsUpdateTime = performance.now();\n }\n numFramesSinceFpsUpdate++;\n // Update render parameters based on key presses.\n if (currentKey) {\n let zInc = 0.025;\n if (currentKey.key == \"ArrowUp\") {\n eyePosition[2] += zInc;\n }\n else if (currentKey.key == \"ArrowDown\") {\n eyePosition[2] -= zInc;\n }\n updateRenderParams();\n }\n const commandEncoder = device.createCommandEncoder();\n // Create the bind group for the compute shader.\n computeBindGroup = device.createBindGroup({\n layout: computePipeline.getBindGroupLayout(0),\n entries: [\n {\n binding: 0,\n resource: {\n buffer: positionsIn,\n },\n },\n {\n binding: 1,\n resource: {\n buffer: positionsOut,\n },\n },\n {\n binding: 2,\n resource: {\n buffer: velocities,\n },\n },\n ],\n });\n let image_a = device.createTexture({\n format: \"r32uint\",\n size: [16, 16],\n usage: GPUTextureUsage.STORAGE_BINDING,\n });\n let image_b = device.createTexture({\n format: \"r32uint\",\n size: [16, 16],\n usage: GPUTextureUsage.STORAGE_BINDING,\n });\n othergroup = device.createBindGroup({\n layout: computePipeline.getBindGroupLayout(1),\n entries: [\n {\n binding: 0,\n resource: image_a.createView(),\n },\n {\n binding: 1,\n resource: image_b.createView(),\n },\n ],\n });\n // Create the bind group for the compute shader.\n renderBindGroup = device.createBindGroup({\n layout: renderPipeline.getBindGroupLayout(0),\n entries: [\n {\n binding: 0,\n resource: {\n buffer: renderParams,\n },\n },\n ],\n });\n if (!paused) {\n // Set up the compute shader dispatch.\n const computePassEncoder = commandEncoder.beginComputePass();\n computePassEncoder.setPipeline(computePipeline);\n computePassEncoder.setBindGroup(0, computeBindGroup);\n computePassEncoder.setBindGroup(1, othergroup);\n computePassEncoder.dispatchWorkgroups(numBodies / workgroupSize);\n computePassEncoder.end();\n // Swap the positions buffers.\n [positionsIn, positionsOut] = [positionsOut, positionsIn];\n }\n // Set up the render pass.\n const colorTexture = canvasContext.getCurrentTexture();\n const colorTextureView = colorTexture.createView();\n const colorAttachment = {\n view: colorTextureView,\n loadOp: \"clear\",\n clearValue: { r: 0, g: 0, b: 0.1, a: 1 },\n storeOp: \"store\",\n };\n const renderPassEncoder = commandEncoder.beginRenderPass({\n colorAttachments: [colorAttachment],\n });\n renderPassEncoder.setPipeline(renderPipeline);\n renderPassEncoder.setViewport(0, 0, canvas.width, canvas.height, 0, 1);\n renderPassEncoder.setScissorRect(0, 0, canvas.width, canvas.height);\n renderPassEncoder.setBindGroup(0, renderBindGroup);\n renderPassEncoder.setVertexBuffer(0, positionsIn);\n renderPassEncoder.draw(6, numBodies);\n renderPassEncoder.end();\n queue.submit([commandEncoder.finish()]);\n requestAnimationFrame(draw);\n}\nconst reset = () => __awaiter(void 0, void 0, void 0, function* () {\n // Make sure WebGPU device has been created.\n if (device == null) {\n yield init();\n }\n // Reset the camera position.\n eyePosition = gl_matrix__WEBPACK_IMPORTED_MODULE_2__.fromValues(0.0, 0.0, -1.5);\n // Reset the simulation.\n numBodies = getSelectedNumber(\"numbodies\");\n initBodies();\n // Recreate pipelines.\n initPipelines();\n paused = false;\n});\nfunction pause() {\n paused = !paused;\n document.getElementById(\"pause\").innerText = paused ? \"Unpause\" : \"Pause\";\n}\nreset();\ndraw();\n// Set up button onclick handlers.\ndocument.querySelector(\"#reset\").addEventListener(\"click\", reset);\ndocument.querySelector(\"#pause\").addEventListener(\"click\", pause);\n// Automatically reset when the number of bodies is changed.\ndocument.querySelector(\"#numbodies\").addEventListener(\"change\", reset);\n// Automatically reset when the power preference is changed.\ndocument.querySelector(\"#powerpref\").addEventListener(\"change\", () => {\n device = null;\n computePipeline = null;\n reset();\n});\n// Automatically rebuild the pipelines when the workgroup size is changed.\ndocument.querySelector(\"#wgsize\").addEventListener(\"change\", initPipelines);\n// Add an event handler to update render parameters when the window is resized.\nwindow.addEventListener(\"resize\", updateRenderParams);\n// Handle key presses for user controls.\ndocument.addEventListener(\"keydown\", (e) => {\n if (e.key == \" \") {\n pause();\n }\n currentKey = e;\n});\ndocument.addEventListener(\"keyup\", (e) => {\n currentKey = null;\n});\n\n\n//# sourceURL=webpack://nbody-webgpu/./src/nbody.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var gl_matrix__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! gl-matrix */ \"./node_modules/gl-matrix/esm/mat4.js\");\n/* harmony import */ var gl_matrix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! gl-matrix */ \"./node_modules/gl-matrix/esm/vec3.js\");\n/* harmony import */ var _shaders_wgsl__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./shaders.wgsl */ \"./src/shaders.wgsl\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\n\n\n// Simulation parameters.\nlet numBodies;\n// Shader parameters.\nlet workgroupSize;\n// Controls.\nlet paused = false;\nlet currentKey = null;\n// Render parameters.\nlet eyePosition;\n// WebGPU objects.\nlet device = null;\nlet queue = null;\nlet computePipeline = null;\nlet renderPipeline = null;\nlet canvas = null;\nlet canvasContext = null;\nlet positionsIn = null;\nlet positionsOut = null;\nlet velocities = null;\nlet renderParams = null;\nlet computeBindGroup = null;\nlet renderBindGroup = null;\nconst init = () => __awaiter(void 0, void 0, void 0, function* () {\n // Initialize the WebGPU device.\n const powerPref = document.getElementById('powerpref');\n const adapter = yield navigator.gpu.requestAdapter({\n powerPreference: powerPref.selectedOptions[0].value,\n });\n device = yield adapter.requestDevice();\n queue = device.queue;\n // Set up the canvas context.\n canvas = document.getElementById('canvas');\n canvasContext = canvas.getContext('webgpu');\n});\n// Generate WGSL shader source.\nfunction getShaders() {\n let preamble = '';\n preamble += `const kWorkgroupSize = ${workgroupSize};\\n`;\n preamble += `const kNumBodies = ${numBodies};\\n`;\n return preamble + _shaders_wgsl__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n}\n// Get the selected number from a drop-down menu.\nfunction getSelectedNumber(id) {\n let list = document.getElementById(id);\n return Number(list.selectedOptions[0].value);\n}\nconst updateRenderParams = () => __awaiter(void 0, void 0, void 0, function* () {\n // Fit the canvas to the window.\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n canvasContext.configure({\n device: device,\n format: navigator.gpu.getPreferredCanvasFormat(),\n usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,\n });\n // Generate the view projection matrix.\n let projectionMatrix = gl_matrix__WEBPACK_IMPORTED_MODULE_1__.create();\n let viewProjectionMatrix = gl_matrix__WEBPACK_IMPORTED_MODULE_1__.create();\n gl_matrix__WEBPACK_IMPORTED_MODULE_1__.perspectiveZO(projectionMatrix, 1.0, canvas.width / canvas.height, 0.1, 50.0);\n gl_matrix__WEBPACK_IMPORTED_MODULE_1__.translate(viewProjectionMatrix, viewProjectionMatrix, eyePosition);\n gl_matrix__WEBPACK_IMPORTED_MODULE_1__.multiply(viewProjectionMatrix, projectionMatrix, viewProjectionMatrix);\n // Write the render parameters to the uniform buffer.\n let renderParamsHost = new ArrayBuffer(4 * 4 * 4);\n let viewProjectionMatrixHost = new Float32Array(renderParamsHost);\n viewProjectionMatrixHost.set(viewProjectionMatrix);\n queue.writeBuffer(renderParams, 0, renderParamsHost);\n});\nfunction initPipelines() {\n // Reset pipelines.\n renderPipeline = null;\n computePipeline = null;\n // Update pipeline options.\n workgroupSize = getSelectedNumber(\"wgsize\");\n // Create a uniform buffer for the render parameters.\n renderParams = device.createBuffer({\n size: 4 * 4 * 4,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n mappedAtCreation: false,\n });\n updateRenderParams();\n // Create the shader module.\n const module = device.createShaderModule({ code: getShaders() });\n // Create the render pipeline.\n const positionsAttribute = {\n shaderLocation: 0,\n offset: 0,\n format: 'float32x4',\n };\n const positionsLayout = {\n attributes: [positionsAttribute],\n arrayStride: 4 * 4,\n stepMode: 'instance',\n };\n renderPipeline = device.createRenderPipeline({\n vertex: {\n module: module,\n entryPoint: 'vs_main',\n buffers: [positionsLayout],\n },\n fragment: {\n module: module,\n entryPoint: 'fs_main',\n targets: [{\n format: navigator.gpu.getPreferredCanvasFormat(),\n blend: {\n color: {\n operation: \"add\",\n srcFactor: \"one\",\n dstFactor: \"one\",\n },\n alpha: {\n operation: \"add\",\n srcFactor: \"one\",\n dstFactor: \"one\",\n },\n }\n }],\n },\n primitive: {\n frontFace: 'cw',\n cullMode: 'none',\n topology: 'triangle-list',\n },\n layout: \"auto\",\n });\n // Create the compute pipeline.\n computePipeline = device.createComputePipeline({\n compute: {\n module: module,\n entryPoint: 'cs_main',\n },\n layout: \"auto\"\n });\n}\nfunction initBodies() {\n // Create buffers for body positions and velocities.\n positionsIn = device.createBuffer({\n size: numBodies * 4 * 4,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX,\n mappedAtCreation: true\n });\n positionsOut = device.createBuffer({\n size: numBodies * 4 * 4,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX,\n mappedAtCreation: false\n });\n velocities = device.createBuffer({\n size: numBodies * 4 * 4,\n usage: GPUBufferUsage.STORAGE,\n mappedAtCreation: false\n });\n // Generate initial positions on the surface of a sphere.\n const kRadius = 0.6;\n let positions = new Float32Array(positionsIn.getMappedRange());\n for (let i = 0; i < numBodies; i++) {\n let longitude = 2.0 * Math.PI * Math.random();\n let latitude = Math.acos((2.0 * Math.random() - 1.0));\n positions[i * 4 + 0] = kRadius * Math.sin(latitude) * Math.cos(longitude);\n positions[i * 4 + 1] = kRadius * Math.sin(latitude) * Math.sin(longitude);\n positions[i * 4 + 2] = kRadius * Math.cos(latitude);\n positions[i * 4 + 3] = 1.0;\n }\n positionsIn.unmap();\n}\n// Render loop.\nconst kFpsUpdateInterval = 500;\nlet numFramesSinceFpsUpdate = 0;\nlet lastFpsUpdateTime = null;\nfunction draw() {\n if (!computePipeline) {\n // Not ready yet.\n requestAnimationFrame(draw);\n return;\n }\n // Update the FPS counter.\n if (lastFpsUpdateTime) {\n const now = performance.now();\n const timeSinceLastLog = now - lastFpsUpdateTime;\n if (timeSinceLastLog >= kFpsUpdateInterval) {\n const fps = numFramesSinceFpsUpdate / (timeSinceLastLog / 1000.0);\n document.getElementById(\"fps\").innerHTML = fps.toFixed(1) + ' FPS';\n lastFpsUpdateTime = performance.now();\n numFramesSinceFpsUpdate = 0;\n }\n }\n else {\n lastFpsUpdateTime = performance.now();\n }\n numFramesSinceFpsUpdate++;\n // Update render parameters based on key presses.\n if (currentKey) {\n let zInc = 0.025;\n if (currentKey.key == 'ArrowUp') {\n eyePosition[2] += zInc;\n }\n else if (currentKey.key == 'ArrowDown') {\n eyePosition[2] -= zInc;\n }\n updateRenderParams();\n }\n const commandEncoder = device.createCommandEncoder();\n // Create the bind group for the compute shader.\n computeBindGroup = device.createBindGroup({\n layout: computePipeline.getBindGroupLayout(0),\n entries: [\n {\n binding: 0,\n resource: {\n buffer: positionsIn,\n },\n },\n {\n binding: 1,\n resource: {\n buffer: positionsOut,\n },\n },\n {\n binding: 2,\n resource: {\n buffer: velocities,\n },\n },\n ],\n });\n // Create the bind group for the compute shader.\n renderBindGroup = device.createBindGroup({\n layout: renderPipeline.getBindGroupLayout(0),\n entries: [\n {\n binding: 0,\n resource: {\n buffer: renderParams,\n },\n },\n ],\n });\n if (!paused) {\n // Set up the compute shader dispatch.\n const computePassEncoder = commandEncoder.beginComputePass();\n computePassEncoder.setPipeline(computePipeline);\n computePassEncoder.setBindGroup(0, computeBindGroup);\n computePassEncoder.dispatchWorkgroups(numBodies / workgroupSize);\n computePassEncoder.end();\n // Swap the positions buffers.\n [positionsIn, positionsOut] = [positionsOut, positionsIn];\n }\n // Set up the render pass.\n const colorTexture = canvasContext.getCurrentTexture();\n const colorTextureView = colorTexture.createView();\n const colorAttachment = {\n view: colorTextureView,\n loadOp: \"clear\",\n clearValue: { r: 0, g: 0, b: 0.1, a: 1 },\n storeOp: 'store'\n };\n const renderPassEncoder = commandEncoder.beginRenderPass({\n colorAttachments: [colorAttachment],\n });\n renderPassEncoder.setPipeline(renderPipeline);\n renderPassEncoder.setViewport(0, 0, canvas.width, canvas.height, 0, 1);\n renderPassEncoder.setScissorRect(0, 0, canvas.width, canvas.height);\n renderPassEncoder.setBindGroup(0, renderBindGroup);\n renderPassEncoder.setVertexBuffer(0, positionsIn);\n renderPassEncoder.draw(6, numBodies);\n renderPassEncoder.end();\n queue.submit([commandEncoder.finish()]);\n requestAnimationFrame(draw);\n}\nconst reset = () => __awaiter(void 0, void 0, void 0, function* () {\n // Make sure WebGPU device has been created.\n if (device == null) {\n yield init();\n }\n // Reset the camera position.\n eyePosition = gl_matrix__WEBPACK_IMPORTED_MODULE_2__.fromValues(0.0, 0.0, -1.5);\n // Reset the simulation.\n numBodies = getSelectedNumber(\"numbodies\");\n initBodies();\n // Recreate pipelines.\n initPipelines();\n paused = false;\n});\nfunction pause() {\n paused = !paused;\n document.getElementById(\"pause\").innerText = paused ? 'Unpause' : 'Pause';\n}\nreset();\ndraw();\n// Set up button onclick handlers.\ndocument.querySelector('#reset').addEventListener('click', reset);\ndocument.querySelector('#pause').addEventListener('click', pause);\n// Automatically reset when the number of bodies is changed.\ndocument.querySelector('#numbodies').addEventListener('change', reset);\n// Automatically reset when the power preference is changed.\ndocument.querySelector('#powerpref').addEventListener('change', () => {\n device = null;\n computePipeline = null;\n reset();\n});\n// Automatically rebuild the pipelines when the workgroup size is changed.\ndocument.querySelector('#wgsize').addEventListener('change', initPipelines);\n// Add an event handler to update render parameters when the window is resized.\nwindow.addEventListener('resize', updateRenderParams);\n// Handle key presses for user controls.\ndocument.addEventListener('keydown', (e) => {\n if (e.key == ' ') {\n pause();\n }\n currentKey = e;\n});\ndocument.addEventListener('keyup', (e) => {\n currentKey = null;\n});\n\n\n//# sourceURL=webpack://nbody-webgpu/./src/nbody.js?"); /***/ })