Google codelab WebGPU入门教程源码<7> - 完整的元胞自动机之生命游戏的完整实现(源码)

对应的教程文章: 

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 WGPUGameOfLife {

	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 = 64;
	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.autoUpdate();
		});
	}
	private mUniformObj: any = {uniformArray: null, uniformBuffer: null};

    private m_timeoutId: any = -1;

    private autoUpdate(): void {
        if (this.m_timeoutId > -1) {
            clearTimeout(this.m_timeoutId);
        }
        this.m_timeoutId = setTimeout(this.autoUpdate.bind(this), 100);// 20 fps
        this.updateWGPUCanvas();
    }
	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) {
			cellStateArray[i] = Math.random() > 0.6 ? 1 : 0;
		}
		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.y)) * u32(grid.x) +
					   (cell.x % u32(grid.x));
			}

			fn cellActive(x: u32, y: u32) -> u32 {
				return cellStateIn[cellIndex(vec2(x, y))];
			}

			@compute @workgroup_size(${sgs}, ${sgs})
			fn computeMain(@builtin(global_invocation_id) cell: vec3u) {

				// Determine how many active neighbors this cell has.
				let activeNeighbors = cellActive(cell.x+1, cell.y+1) +
										cellActive(cell.x+1, cell.y) +
										cellActive(cell.x+1, cell.y-1) +
										cellActive(cell.x, cell.y-1) +
										cellActive(cell.x-1, cell.y-1) +
										cellActive(cell.x-1, cell.y) +
										cellActive(cell.x-1, cell.y+1) +
										cellActive(cell.x, cell.y+1);
				//
				let i = cellIndex(cell.xy);

				// Conway's game of life rules:
				switch activeNeighbors {
					case 2: { // Active cells with 2 neighbors stay active.
						cellStateOut[i] = cellStateIn[i];
					}
					case 3: { // Cells with 3 neighbors become or stay active.
						cellStateOut[i] = 1;
					}
					default: { // Cells with < 2 or > 3 neighbors become inactive.
						cellStateOut[i] = 0;
					}
				}
			}`
		});

		return simulationShaderModule;
	}

	private createRectGeometryData(device: any): void {

		let hsize = 0.8;
		let vertices = new Float32Array([
		//   X,    Y,
			-hsize, -hsize, // Triangle 1 (Blue)
			 hsize, -hsize,
			 hsize,  hsize,

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

		let vertexBuffer = device.createBuffer({
			label: "Cell vertices",
			size: vertices.byteLength,
			usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
		});
		device.queue.writeBuffer(vertexBuffer, /*bufferOffset=*/0, vertices);

		this.mRVertices = vertices;
		this.mVtxBuffer = vertexBuffer;
	}
	private renderpass(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) {

			this.createRectGeometryData( device );

			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 {
				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",
				}
			});

			vertices = this.mRVertices;
			vertexBuffer = this.mVtxBuffer;

			this.mRPipeline = cellPipeline;
			this.mRSimulationPipeline = simulationPipeline;
		}

		const bindGroups = this.mUniformBindGroups;


		pass.setPipeline(cellPipeline);
		pass.setVertexBuffer(0, vertexBuffer);

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

		pass.end();


		computePass.setPipeline(simulationPipeline),
		computePass.setBindGroup(0, bindGroups[this.mStep % 2]);
		const workgroupCount = Math.ceil(this.mGridSize / this.mShdWorkGroupSize);
		computePass.dispatchWorkgroups(workgroupCount, workgroupCount);
		computePass.end();
		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.renderpass(device, pass, computePass);


		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/156086.html

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

相关文章

小白也想写综述(一)

前言 在选择科研方向时&#xff0c;考虑自己的兴趣和职业目标是非常重要的&#xff1a; 综述论文的价值&#xff1a;撰写综述论文&#xff0c;尤其是在深度强化学习和区块链这样的前沿技术领域&#xff0c;能够帮助建立扎实的理论基础&#xff0c;并且对整个领域有一个全面的认…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷4

单选题 1、Soda is sold in packs of 6、12 and 24 cans.What is the minimum number of packs needed to buy exactly 90cans of soda? A、4 B、5 C、6 D、8 答案&#xff1a;B 2、蓝桥杯STEMA考试中心发送有同样内容、重量、体积的试卷&#xff0c;从北京10箱和上海6箱…

Reflect 对象的创建目的

目录 前言 逻辑Reflect对象的创建目的包括&#xff1a; 代码示例 使用 Reflect 操作属性 使用 Reflect 检查属性是否存在 使用 Reflect 创建代理 用法 Reflect对象的用法包括&#xff1a; 用于读取和设置对象属性的方法&#xff1a;Reflect.get(obj, prop)、Reflect.s…

等级保护建设全流程

等保&#xff0c;全称为信息安全等级保护&#xff0c;是对信息和信息载体按照重要性等级分级进行保护的一种工作。 企业的信息系统有收集、储存用户信息的&#xff0c;都需要进行等保建设&#xff0c;以此来整改提升系统的安全防护能力&#xff0c;降低被攻击的风险。若不然一旦…

通付盾Web3专题 | KYT/AML:Web3合规展业的必要条件

与传统证券一样&#xff0c;基于区块链技术发展出来的虚拟资产交易所经历了快速发展而缺乏有效监管的行业早期。除了科技光环加持的各种区块链项目方、造富神话之外&#xff0c;交易所遭到黑客攻击、内部偷窃作恶、甚至经营主体异常而致使投资人血本无归的案例亦令人触目惊心。…

PostgreSQL技术大讲堂 - 第34讲:调优工具pgBagder部署

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第34讲&#…

Consumer的负载均衡

想要提高Consumer的处理速度&#xff0c;可以启动多个Consumer并发处理&#xff0c;这个时候就涉及如何在多个Consumer之间负载均衡的问题&#xff0c;接下来结合源码分析Consumer的负载均衡实现。 要做负载均衡&#xff0c;必须知道一些全局信息&#xff0c;也就是一个Consum…

Accelerate 0.24.0文档 四:Megatron-LM

参考《Megatron-LM》 文章目录 一、Megatron-LM集成简介二、环境配置设置conda环境的步骤&#xff1a; 二、Accelerate Megatron-LM Plugin三、自定义训练过程四、检查点转换五、文本生成六、支持ROPE 、 ALiBi和Multi-Query Attention七、注意事项 一、Megatron-LM集成简介 在…

SystemVerilog学习(8)——包的使用

目录 一、包的定义 二、导出包的内容 1、可以通过域的索引符::号直接引用 2、可以指定索引一些需要的包中定义的类型到指定的容器中 3、通过通配符*来将包中所有的类别导入到指定容器中 三、包的使用 在进行本文的学习之前&#xff0c;首先需要对SV中类相关的内容有充分的认识…

mount /dev/mapper/centos-root on sysroot failed处理

今天发现centos7重启开不进去系统 通过查看日志主要告警如下 修复挂载目录 xfs_repair /dev/mapper/centos-root不行加-L参数 xfs_repair -L /dev/mapper/centos-root重启 reboot

2023年中国开式冷却塔应用现状及行业市场规模前景分析[图]

开式塔是目前应用最广、类型最多的一种冷却系统。循环水移走工艺介质或换热设备所散发的热量后成为热水&#xff0c;热水进入冷却塔后和空气直接接触&#xff0c;大部分热水得到冷却后&#xff0c;再循环使用。开式冷却塔又可以分为逆流式冷却塔和横流式冷却塔&#xff0c;按照…

基于Vue+SpringBoot的海南旅游景点推荐系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

【Linux】一

本文使用的是云服务器来获取Linux环境 (使用虚拟机同样可以学习使用命令), 并且介绍了常用的Linux 命令. 获取Linux环境 使用xshell连接到云服务器 1.新建会话 输入主机号(云服务器的外网ip) 2.输入用户名/密码 centos的用户名:root 密码就是在后台设置的 3.成功进入 ~描…

01ctfer 文件上传

01ctfer 文件上传 启动靶场 访问该地址 代码审计 <?php header("Content-Type:text/html; charsetutf-8"); // 每5分钟会清除一次目录下上传的文件 require_once(pclzip.lib.php);if(!$_FILES){echo <!DOCTYPE html> <html lang"zh">…

【Linux】Linux进程间通信(二)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【Linux】…

VUE指令、computed计算属性和watch 侦听器(附带详细案例)

文章目录 前言一、指令补充1. 指令修饰符2. v-bind对于样式操作的增强 - class3. 案例 - 京东秒杀 tab 导航高亮4. v-bind对于样式操作的增强 - style5. v-model应用于其他表单元素 二、computed计算属性1. 基础语法2. 计算属性 vS method 方法3. 完整写法4. 成绩案例 三、watc…

android PopupWindow设置

记录一个小功能&#xff0c;使用场景&#xff0c;列表项点击弹出 如图&#xff1a; java类代码&#xff1a; public class PopupUtil extends PopupWindow {private Activity context;private View view;private ListView listView;private TextView m_tv_reminderm, m_tv_Wa…

4.1 Windows驱动开发:内核中进程与句柄互转

在内核开发中&#xff0c;经常需要进行进程和句柄之间的互相转换。进程通常由一个唯一的进程标识符&#xff08;PID&#xff09;来标识&#xff0c;而句柄是指对内核对象的引用。在Windows内核中&#xff0c;EProcess结构表示一个进程&#xff0c;而HANDLE是一个句柄。 为了实…

Java集合List报错,java.lang.UnsupportedOperationException

目录 一、点击Arrays.asList源码&#xff0c;一探究竟二、习惯了Arrays.asList&#xff0c;就是想用.add()添加元素&#xff0c;怎么办&#xff1f;三、又有一个同事&#xff0c;是这样写的四、重新点击Arrays.asList源码&#xff0c;一探究竟五、全是坑&#xff0c;怎么办&…

muduo源码剖析之TcpServer服务端

简介 TcpServer拥有Acceptor类&#xff0c;新连接到达时new TcpConnection后续客户端和TcpConnection类交互。TcpServer管理连接和启动线程池&#xff0c;用Acceptor接受连接。 服务端封装 - muduo的server端维护了多个tcpconnection 注意TcpServer本身不带Channel&#xff0…