Files
WebGPU-FFT-Ocean-Demo/AGENTS.md
2025-12-31 14:31:55 +01:00

7.6 KiB

Below is a clean, concise AGENTS.md “WebGPU Patterns” section designed specifically for your custom framework:

  • Engine
  • RenderPipeline
  • Block
  • RenderPass
  • Shader

Not the GPT model logic. Not embeddings or inference. Only the architectural usage patterns you want Codex to follow.

You can copy this directly into your existing AGENTS.md under “Example Patterns”, or use it to replace that section entirely.

It teaches Codex:

  • How your framework is structured
  • How passes are created
  • How pipelines are assembled
  • How shader setup works
  • How buffer binding works
  • How execution is called

This ensures that Codex will generate compatible new passes, pipelines, and engine bootstraps.


WEBGPU FRAMEWORK PATTERNS (SHORT, STRICT)

Codex must follow these exact patterns when creating or modifying WebGPU code using the framework located in /framework.

These patterns describe:

  • how to extend RenderPass
  • how to assemble a Block
  • how to create a RenderPipeline
  • how to use Engine
  • how to load WGSL shaders
  • how to bind buffers and call .execute()

No ML logic included. Only architectural usage patterns.


paths

/framework/RenderPass.js

/framework/WebGpu.js

/framework/Block.js

/framework/EventManager.js

/framework/Camera.js

/framework/Matrix4.js

/framework/vector3.js

1. RenderPass Pattern

A pass is always:

  • its own file under passes/
  • a class extending RenderPass
  • has create(), bindBuffers(), execute()
  • loads its shader using new Shader(this.device)
  • sets WGSL variables using .setVariable()
import { RenderPass }	from "framework/RenderPass.js";

import Shader			from "framework/WebGpu.js";


export class ExamplePass extends RenderPass {

	async create( ) {

		this.shader			= new Shader( this.device );

		await this.shader.setup( "shaders/example.wgsl" );

	}


	bindBuffers( ) {

		var inputData = this.pipeline.memory.inputData;

		// set variable can be normal javascript arrays, also typed arrays.
		this.shader.setVariable(
			"input1",
			[1,2,3,4,5]
		);


		// set variable can be normal javascript arrays, also typed arrays.
		this.shader.setVariable(
			"input2",
			new Float32Array( [ 1,2,3,4 ] ) 
		);

		const texture = this.shader.createTextureFromData(
											width,
											height,
											[ 1,2,3... ]
										);

		this.shader.setVariable( "texture", texture );


		// associate buffer from other shader buffer
		const prevLayerBlock 	= this.pipeline.getBlockByName("blockName");

		const pass    			= prevLayerBlock.getPass("passName");

		const buffer 	 		= pass.shader.getBuffer("outputVector");
		
		this.shader.setBuffer("inputHiddenState", buffer);

	}


	async execute( ) {

		await this.shader.execute( 1 );

	}

}

Rules Codex must preserve:

  • One blank line between imports.
  • Tab-aligned imports before each from.
  • No arrow functions.
  • await this.shader.setup( path ).
  • this.shader.setVariable( name, data ) only in bindBuffers() or incremental calls.
  • execute() always calls this.shader.execute( workgroups ).

2. Block Pattern

A block is a logical grouping of passes. Codex must:

  • create blocks using new Block( "name", pipeline )
  • attach passes with block.addPass( "Name", instance )
  • add block to pipeline with pipeline.addBlock( block )
import { Block }	from "./framework/Block.js";

import { ExamplePass }	from "../passes/ExamplePass.js";


const block		= new Block( "example", this );

const pass		= new ExamplePass( );

block.addPass( "Example", pass );

this.addBlock( block );

3. Pipeline Pattern

A pipeline:

  • extends RenderPipeline
  • constructs blocks in create()
  • allocates memory buffers on this.memory
  • always calls await super.create() last
  • may override execute() to chain passes logically
import { RenderPipeline }	from "./framework/RenderPipeline.js";

import { Block }			from "./framework/Block.js";

import { ExamplePass }		from "../passes/ExamplePass.js";


export class ExamplePipeline extends RenderPipeline {

	async create( ) {

		/* Allocate global memory */
		this.memory.input			= new Float32Array( 1024 );

		this.memory.output			= new Float32Array( 1024 );


		/* Pass block */
		const block					= new Block( "example", this );

		const examplePass			= new ExamplePass( );

		block.addPass( "Example", examplePass );

		this.addBlock( block );


		/* Build GPU resources (mandatory) */
		await super.create();

	}


	async execute( ) {

		const block					= this.blocks[0];

		const pass					= block.getPass( "Example" );

		await pass.execute();

	}

}

3. Camera and eventmanager Pattern

import Matrix4 			from "framework/Matrix4.js"

import Vector3 			from "framework/Vector3.js"


		this.camera 			= new Camera( [0, 0, 1115], [0, -.3, 0], [0, 1, 0] );

		this.eventManager.setup( canvas, this.camera );



		const viewMatrixData 			= this.camera.getViewMatrix();

		const projectionMatrixData 		= Matrix4.createProjectionMatrix( this.camera, this.canvas )

		const viewProjectionMatrix 		= Matrix4.multiply( projectionMatrixData, viewMatrixData );

		const cameraWorldMatrix 		= Matrix4.invert( viewMatrixData );  

		const cameraPosition			= Matrix4.getColumn( cameraWorldMatrix, 3 );

		this.renderShader.setVariable( "viewProjectionMatrix", viewProjectionMatrix );

		this.renderShader.setVariable( "cameraPosition", cameraPosition );


Codex must:

  • allocate buffers on this.memory.*
  • assemble blocks before super.create()
  • not reorder pipeline structure automatically

4. Engine Usage Pattern

The entrypoint must follow this shape whenever Codex creates a new WebGPU project:

import { Engine }	from "./framework/Engine.js";

import { ExamplePipeline }	from "./pipelines/ExamplePipeline.js";


async function main( ) {

	
	const adapter				= await navigator.gpu.requestAdapter( );

	const device				= await adapter.requestDevice( );

	const engine				= new Engine( device );


	/* Pipeline */
	const pipeline				= new ExamplePipeline( engine );

	pipeline.memory.set( "inputData", new Float32Array( 1024 ) );


	await pipeline.create();


	await pipeline.bindBuffers();


	await pipeline.execute();


}

main( );

Codex must always:

  • request adapter → requestDevice
  • create new Engine(device)
  • instantiate pipeline with (engine)
  • set memory with pipeline.memory.set( key, value )
  • call await pipeline.create()
  • call await pipeline.bindBuffers()
  • call await pipeline.execute()

5. Shader Usage Pattern

Codex must use the Shader class exactly like this:

this.shader		= new Shader( this.device );

await this.shader.setup( "shaders/example.wgsl" );

this.shader.setVariable( "bufferA", floatArray );

await this.shader.execute( workgroups );

Always:

  • One shader instance per pass
  • WGSL path passed to .setup()
  • .setVariable() before .execute()
  • No arrow functions
  • Spaces inside parentheses

6. Memory Allocation Pattern

All buffers are allocated as plain typed arrays under:

this.memory.X = new Float32Array( size );

Codex must never use ArrayBuffer directly unless required.


7. Execution Flow Rule

Every pipeline execution must follow:

  1. await pipeline.create()
  2. await pipeline.bindBuffers()
  3. await pipeline.execute()

Never run execute before create. Never omit bindBuffers if pass needs it.


8. Codex MUST NOT Generate

  • Raw WebGPU API calls (device.createBuffer) unless inside Shader class

  • Inline WGSL; always stored in .wgsl files

  • Arrow functions

  • Inline callbacks

  • Different architecture than this:

    • Engine
    • Pipeline
    • Block
    • RenderPass
    • Shader