Google codelab WebGPU入门教程源码<6> - 使用计算着色器实现计算元胞自动机之生命游戏模拟过程(源码)

对应的教程文章: 

https://codelabs.developers.google.com/your-first-webgpu-app?hl=zh-cn#7

对应的源码执行效果:

对应的教程源码: 

此处源码和教程本身提供的部分代码可能存在一点差异。点击画面,切换效果。

class Color4 {

	r: number;
	g: number;
	b: number;
	a: number;

	constructor(pr = 1.0, pg = 1.0, pb = 1.0, pa = 1.0) {
		this.r = pr;
		this.g = pg;
		this.b = pb;
		this.a = pa;
	}
}

export class WGPUSimulation {

	private mRVertices: Float32Array = null;
	private mRPipeline: any | null = null;
	private mRSimulationPipeline: any | null = null;
	private mVtxBuffer: any | null = null;
	private mCanvasFormat: any | null = null;
	private mWGPUDevice: any | null = null;
	private mWGPUContext: any | null = null;
	private mUniformBindGroups: any | null = null;
	private mGridSize = 32;
	private mShdWorkGroupSize = 8;
	constructor() {}
	initialize(): void {

		const canvas = document.createElement("canvas");
		canvas.width = 512;
		canvas.height = 512;
		document.body.appendChild(canvas);
		console.log("ready init webgpu ...");
		this.initWebGPU(canvas).then(() => {
			console.log("webgpu initialization finish ...");

			this.updateWGPUCanvas();
		});
		document.onmousedown = (evt):void => {
			this.updateWGPUCanvas( new Color4(0.05, 0.05, 0.1) );
		}
	}
	private mUniformObj: any = {uniformArray: null, uniformBuffer: null};

	private createStorage(device: any): any {
		// Create an array representing the active state of each cell.
		const cellStateArray = new Uint32Array(this.mGridSize * this.mGridSize);
		// Create two storage buffers to hold the cell state.
		const cellStateStorage = [
			device.createBuffer({
				label: "Cell State A",
				size: cellStateArray.byteLength,
				usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
			}),
			device.createBuffer({
				label: "Cell State B",
				size: cellStateArray.byteLength,
				usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
			})
		];
		// Mark every third cell of the first grid as active.
		for (let i = 0; i < cellStateArray.length; i+=3) {
			cellStateArray[i] = 1;
		}
		device.queue.writeBuffer(cellStateStorage[0], 0, cellStateArray);
		// Mark every other cell of the second grid as active.
		for (let i = 0; i < cellStateArray.length; i++) {
			cellStateArray[i] = i % 2;
		}
		device.queue.writeBuffer(cellStateStorage[1], 0, cellStateArray);

		return cellStateStorage;
	}

	private createUniform(device: any): any {
		// Create a uniform buffer that describes the grid.
		const uniformArray = new Float32Array([this.mGridSize, this.mGridSize]);
		const uniformBuffer = device.createBuffer({
			label: "Grid Uniforms",
			size: uniformArray.byteLength,
			usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
		});
		device.queue.writeBuffer(uniformBuffer, 0, uniformArray);


		const cellStateStorage = this.createStorage(device);

		// Create the bind group layout and pipeline layout.
		const bindGroupLayout = device.createBindGroupLayout({
			label: "Cell Bind Group Layout",
			entries: [{
				binding: 0,
				visibility: GPUShaderStage.FRAGMENT | GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
				buffer: {} // Grid uniform buffer
			}, {
				binding: 1,
				visibility: GPUShaderStage.VERTEX | GPUShaderStage.COMPUTE,
				buffer: { type: "read-only-storage"} // Cell state input buffer
			}, {
				binding: 2,
				visibility: GPUShaderStage.COMPUTE,
				buffer: { type: "storage"} // Cell state output buffer
			}]
		});
		const bindGroups = [
			device.createBindGroup({
				label: "Cell renderer bind group A",
				layout: bindGroupLayout,
				entries: [
					{
						binding: 0,
						resource: { buffer: uniformBuffer }
					}, {
						binding: 1,
						resource: { buffer: cellStateStorage[0] }
					}, {
						binding: 2,
						resource: { buffer: cellStateStorage[1] }
					}
				],
			}),
			device.createBindGroup({
				label: "Cell renderer bind group B",
				layout: bindGroupLayout,
				entries: [
					{
						binding: 0,
						resource: { buffer: uniformBuffer }
					}, {
						binding: 1,
						resource: { buffer: cellStateStorage[1] }
					}, {
						binding: 2,
						resource: { buffer: cellStateStorage[0] }
					}
				],
			})
		];

		this.mUniformBindGroups = bindGroups;

		const obj = this.mUniformObj;
		obj.uniformArray = uniformArray;
		obj.uniformBuffer = uniformBuffer;

		return bindGroupLayout;
	}
	private mStep = 0;
	private createComputeShader(device: any): any {
		let sgs = this.mShdWorkGroupSize;
		// Create the compute shader that will process the simulation.
		const simulationShaderModule = device.createShaderModule({
			label: "Game of Life simulation shader",
			code: `
			@group(0) @binding(0) var<uniform> grid: vec2f;

			@group(0) @binding(1) var<storage> cellStateIn: array<u32>;
			@group(0) @binding(2) var<storage, read_write> cellStateOut: array<u32>;

			fn cellIndex(cell: vec2u) -> u32 {
				return cell.y * u32(grid.x) + cell.x;
			}

			@compute @workgroup_size(${sgs}, ${sgs})
			fn computeMain(@builtin(global_invocation_id) cell: vec3u) {
				if (cellStateIn[cellIndex(cell.xy)] == 1) {
					cellStateOut[cellIndex(cell.xy)] = 0;
				} else {
					cellStateOut[cellIndex(cell.xy)] = 1;
				}
			}`
		});

		return simulationShaderModule;
	}
	private createRectGeometryData(device: any, pass: any, computePass: any): void {

		let vertices = this.mRVertices;
		let vertexBuffer = this.mVtxBuffer;
		let cellPipeline = this.mRPipeline;
		let simulationPipeline = this.mRSimulationPipeline;
		if(!cellPipeline) {
			let hsize = 0.8;
			vertices = new Float32Array([
			//   X,    Y,
				-hsize, -hsize, // Triangle 1 (Blue)
				 hsize, -hsize,
				 hsize,  hsize,

				-hsize, -hsize, // Triangle 2 (Red)
				 hsize,  hsize,
				-hsize,  hsize,
			]);

			vertexBuffer = device.createBuffer({
				label: "Cell vertices",
				size: vertices.byteLength,
				usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
			});
			device.queue.writeBuffer(vertexBuffer, /*bufferOffset=*/0, vertices);
			const vertexBufferLayout = {
				arrayStride: 8,
				attributes: [{
					format: "float32x2",
					offset: 0,
					shaderLocation: 0, // Position, see vertex shader
				}],
			};
			const shaderCodes = `
			struct VertexInput {
				@location(0) pos: vec2f,
				@builtin(instance_index) instance: u32,
			};

			struct VertexOutput {
				@builtin(position) pos: vec4f,
				@location(0) cell: vec2f,
			};

			@group(0) @binding(0) var<uniform> grid: vec2f;
			@group(0) @binding(1) var<storage> cellState: array<u32>;

			@vertex
			fn vertexMain(input: VertexInput) -> VertexOutput  {
				let i = f32(input.instance);
				let cell = vec2f(i % grid.x, floor(i / grid.x));
				let cellOffset = cell / grid * 2;

				let state = f32(cellState[input.instance]);
				let gridPos = (input.pos * state + 1) / grid - 1 + cellOffset;

				var output: VertexOutput;
				output.pos = vec4f(gridPos, 0, 1);
				output.cell = cell;
				return output;
			}

			@fragment
			fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
				// return vec4f(input.cell, 0, 1);
				let c = input.cell/grid;
				return vec4f(c, 1.0 - c.x, 1);
			}
			`;
			const bindGroupLayout = this.createUniform(device);
			const pipelineLayout = device.createPipelineLayout({
				label: "Cell Pipeline Layout",
				bindGroupLayouts: [ bindGroupLayout ],
			});
			const cellShaderModule = device.createShaderModule({
				label: "Cell shader",
				code: shaderCodes
				});
			cellPipeline = device.createRenderPipeline({
				label: "Cell pipeline",
				layout: pipelineLayout,
				vertex: {
					module: cellShaderModule,
					entryPoint: "vertexMain",
					buffers: [vertexBufferLayout]
				},
				fragment: {
					module: cellShaderModule,
					entryPoint: "fragmentMain",
					targets: [{
						format: this.mCanvasFormat
					}]
				},
			});

			const simulationShaderModule = this.createComputeShader( device );
			// Create a compute pipeline that updates the game state.
			simulationPipeline = device.createComputePipeline({
					label: "Simulation pipeline",
					layout: pipelineLayout,
					compute: {
					module: simulationShaderModule,
					entryPoint: "computeMain",
				}
			});
			this.mRVertices = vertices;
			this.mVtxBuffer = vertexBuffer;
			this.mRPipeline = cellPipeline;
			this.mRSimulationPipeline = simulationPipeline;
		}
		const bindGroups = this.mUniformBindGroups;

		computePass.setPipeline(simulationPipeline),
		computePass.setBindGroup(0, bindGroups[this.mStep % 2]);
		const workgroupCount = Math.ceil(this.mGridSize / this.mShdWorkGroupSize);
		computePass.dispatchWorkgroups(workgroupCount, workgroupCount);

		pass.setPipeline(cellPipeline);
		pass.setVertexBuffer(0, vertexBuffer);
		// pass.setBindGroup(0, this.mUniformBindGroup);
		pass.setBindGroup(0, bindGroups[this.mStep % 2]);
		pass.draw(vertices.length / 2, this.mGridSize * this.mGridSize);

		this.mStep ++;
	}

	private updateWGPUCanvas(clearColor: Color4 = null): void {

		clearColor = clearColor ? clearColor : new Color4(0.05, 0.05, 0.1);
		const device = this.mWGPUDevice;
		const context = this.mWGPUContext;
		const rpassParam = {
			colorAttachments: [
				{
					clearValue: clearColor,
					// clearValue: [0.3,0.7,0.5,1.0], // yes
					view: context.getCurrentTexture().createView(),
					loadOp: "clear",
					storeOp: "store"
				}
			]
		};

		const encoder = device.createCommandEncoder();
		const pass = encoder.beginRenderPass( rpassParam );

		const computeEncoder = device.createCommandEncoder();
		const computePass = computeEncoder.beginComputePass()

		this.createRectGeometryData(device, pass, computePass);

		pass.end();
		computePass.end();

		device.queue.submit([ encoder.finish() ]);
		device.queue.submit([ computeEncoder.finish() ]);
	}
	private async initWebGPU(canvas: HTMLCanvasElement) {
		const gpu = (navigator as any).gpu;
		if (gpu) {
			console.log("WebGPU supported on this browser.");

			const adapter = await gpu.requestAdapter();
			if (adapter) {
				console.log("Appropriate GPUAdapter found.");
				const device = await adapter.requestDevice();
				if (device) {
					this.mWGPUDevice = device;
					console.log("Appropriate GPUDevice found.");
					const context = canvas.getContext("webgpu") as any;
					const canvasFormat = gpu.getPreferredCanvasFormat();
					this.mWGPUContext = context;
					this.mCanvasFormat = canvasFormat;
					console.log("canvasFormat: ", canvasFormat);
					context.configure({
						device: device,
						format: canvasFormat,
						alphaMode: "premultiplied"
					});
				} else {
					throw new Error("No appropriate GPUDevice found.");
				}
			} else {
				throw new Error("No appropriate GPUAdapter found.");
			}
		} else {
			throw new Error("WebGPU not supported on this browser.");
		}
	}
	run(): void {}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/152941.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

挑战字节软件测试岗,原来这么轻松...

当前就业环境&#xff0c;裁员、失业消息满天飞&#xff0c;好像有一份工作就不错了&#xff0c;更别说高薪了。其实这只是一方面&#xff0c;而另一方面&#xff0c;各大企业依然求贤若渴&#xff0c;高技术人才依然紧缺&#xff0c;只要你技术过硬&#xff0c;拿个年薪50w不是…

锁之间的故事

目录 常用锁策略 1.乐观锁 VS 悲观锁 2.轻量级锁 VS 重量级锁 3.自旋锁 VS 挂起等待锁 4.互斥锁 VS 读写锁 5.公平锁 VS 非公平锁 6.可重入锁 VS 可重入锁 CAS ABA问题 Synchronized原理 1. 锁升级/锁膨胀 2.锁消除 3.锁粗化 常用锁策略 1.乐观锁 VS 悲观锁 站在…

二叉树相关

一、概念 二、题目 2.1 把数组转换成二叉树 2.2.1 使用队列方式 public static Node getTreeFromArr2(int[] arr) {if (arr null || arr.length 0) {return null;}LinkedList<Node> quque new LinkedList<>();Node root new Node(arr[0]);quque.add(root);in…

有大量虾皮买家号想防关联该怎么做?

Shopee平台规定一个买家只能拥有一个买家号&#xff0c;如果一台电脑或者一个手机同时登录好几个买家号&#xff0c;那么很有可能就会关联封号的。那么有大量虾皮买家号想防关联该怎么做&#xff1f; 如果想要运用大量的shopee买家号来操作&#xff0c;那么需要使用有防指纹技术…

利用vscode连接远程服务器进行代码调试

文章目录 一、vscode下载二、连接服务器1. 安装remote development套件2. 配置ssh3. 连接服务器4. 打开服务器文件路径 三、支持GUI显示1. windows系统安装xserver服务&#xff1a;可以用xming或VcXsrv2. windows系统(安装了vscode的系统)下安装插件3. vscode实现免密登录远程服…

<蓝桥杯软件赛>零基础备赛20周--第6周--数组和队列

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

Ansys Speos | 如何利用Speos联合optiSLang进行光导优化设计

在本例中&#xff0c;我们将使用 Speos 和 optiSLang 实现光导的设计优化&#xff0c;以实现汽车日行灯、内饰氛围灯等的光导设计&#xff0c;并改善光导亮度的均匀性&#xff0c;以自动优化设计的方式实现更好的照明外观。 概述 在汽车照明应用中&#xff0c;日行灯是一个独特…

[文件读取]Grafana未授权任意文件读取

1.1漏洞描述 漏洞编号Grafana未授权任意文件读取漏洞类型文件读取漏洞等级⭐⭐⭐漏洞环境VULFOCUS攻击方式 描述: Grafana是一个跨平台、开源的数据可视化网络应用程序平台。用户配置连接的数据源之后&#xff0c;Grafana可以在网络浏览器里显示数据图表和警告。 Grafana 存在…

【自定义列表头】vue el-table表格自定义列显示隐藏,多级表头自定义列显示隐藏,自由搭配版本和固定列版本【注释详细】

前言 功能介绍 最近遇到一个功能&#xff0c;需要的是把表格的列可以配置&#xff0c; 用户可以根据自己想要看的数据来改变表头列显示哪些隐藏哪些。 于是我做了两个版本。第一个版本是自由搭配的。 就是提前顶号所有的列&#xff0c;然后自己可以拖拽到想要位置顺序。 也可以…

打开IE浏览器

原文地址&#xff1a;https://www.xiaoheiwoo.com/windows-11-internet-explorer/#:~:text%E5%A6%82%E4%BD%95%E5%9C%A8%20Windows11%20%E4%B8%AD%E5%90%AF%E7%94%A8%20IE%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%843%E7%A7%8D%E6%96%B9%E6%B3%95%201%20%E6%96%B9%E6%B3%95%E4%B8%80…

俄罗斯方块

一.准备工作 先创建一个新的Java项目命名为“俄罗斯方块”。再在该项目中创建一个文件夹命名为”images”&#xff0c;并将所需的图片素材拖入该文件夹。 二.代码呈现 编写小方块类&#xff1a; import java.awt.image.BufferedImage;/*** 描述:小方块类* 属性&#xff1a;…

Nas搭建webdav服务器并同步Zotero科研文献

无需云盘&#xff0c;不限流量实现Zotero跨平台同步&#xff1a;内网穿透私有WebDAV服务器 文章目录 无需云盘&#xff0c;不限流量实现Zotero跨平台同步&#xff1a;内网穿透私有WebDAV服务器一、Zotero安装教程二、群晖NAS WebDAV设置三、Zotero设置四、使用公网地址同步Zote…

不会代码的时候,如何使用Jmeter完成接口测试?

1.接口测试简介 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 2.接口测试流程 接口测试…

推荐一个非常好用的uniapp的组件库【TMUI3.0】

文章目录 前言官网地址如何使用&#xff1f;注意事项后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;前端系列文章 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果…

Java虚拟机运行时数据区结构详解

Java虚拟机运行时数据区结构如图所示 程序计数器 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。 多线程切换时&#xff0c;为了能恢复到正确的执行位置&#xff0c;每条线程…

[Python学习笔记]Python 性能分析

在上一章节 [Python学习笔记]Requests性能优化之Session 中&#xff0c;通过在 Resquests 中使用 session&#xff0c;将 Python 脚本的运行效率提升了 3 倍。但当时对问题的排查主要是基于程序实现逻辑的推断&#xff0c;并没有实质性的证据。 本次使用 Python 的性能分析工具…

【每日一题】最长奇偶子数组

文章目录 Tag题目来源解题思路方法一&#xff1a;枚举方法二&#xff1a;一次遍历 其他语言python3 写在最后 Tag 【一次遍历】【枚举】【数组】【2023-11-16】 题目来源 2760. 最长奇偶子数组 解题思路 方法一&#xff1a;枚举 本题有多种方法可以解决&#xff0c;最朴素的…

如何有效防止公司内部的信息泄露?

信息泄露对公司可能带来严重影响&#xff0c;因此采取一系列措施以确保信息安全至关重要。以下是一些建议&#xff1a; 部署综合的防泄密软件&#xff1a; 在公司内部&#xff0c;使用专业的防泄密软件如华企盾DSC系统&#xff0c;涵盖文件加密、U盘管控、桌面行为管理、日志审…

极智AI | Realtime Multi-Person人体姿态估计之OpenPose

欢迎关注我的公众号 [极智视界],获取我的更多经验分享 大家好,我是极智视界,本文来介绍一下 Realtime Multi-Person人体姿态估计之OpenPose。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码下载,链接:https://t.zsxq.com/0aiNxERDq OpenPose 主要是…

qt使用AES加密、解密字符串

一、AES算法 AES (Advanced Encryption Standard) 是一种对称加密算法&#xff0c;是目前被广泛使用的数据加密标准之一。该算法旨在取代DES (Data Encryption Standard) 算法。AES最初由比利时密码学家 Joan Daemen 和 Vincent Rijmen 提出&#xff0c;经过多年的演化、改进和…