more cleaning up
This commit is contained in:
parent
2c7d72a699
commit
560398fefb
110 changed files with 931 additions and 2645 deletions
|
|
@ -7,8 +7,6 @@ struct Settings {
|
|||
|
||||
struct Counters {
|
||||
aliveAgentCount: atomic<u32>,
|
||||
padding0: atomic<u32>,
|
||||
padding1: atomic<u32>,
|
||||
};
|
||||
|
||||
@group(1) @binding(0) var<uniform> settings: Settings;
|
||||
|
|
@ -22,10 +20,9 @@ var<workgroup> workgroupCopyCount: u32;
|
|||
@compute @workgroup_size(64)
|
||||
fn main(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
@builtin(local_invocation_id) local_id: vec3<u32>,
|
||||
@builtin(num_workgroups) workgroup_count: vec3<u32>
|
||||
@builtin(local_invocation_id) local_id: vec3<u32>
|
||||
) {
|
||||
let id = get_id(global_id, workgroup_count);
|
||||
let id = get_id(global_id);
|
||||
|
||||
if local_id.x == 0u {
|
||||
atomicStore(&workgroupAliveCount, 0u);
|
||||
|
|
@ -65,10 +62,9 @@ fn main(
|
|||
@compute @workgroup_size(64)
|
||||
fn copyCompactedAgents(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
@builtin(local_invocation_id) local_id: vec3<u32>,
|
||||
@builtin(num_workgroups) workgroup_count: vec3<u32>
|
||||
@builtin(local_invocation_id) local_id: vec3<u32>
|
||||
) {
|
||||
let id = get_id(global_id, workgroup_count);
|
||||
let id = get_id(global_id);
|
||||
|
||||
if local_id.x == 0u {
|
||||
workgroupCopyCount = atomicLoad(&counters.aliveAgentCount);
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
struct Settings {
|
||||
agentCount: u32 // might be smaller than the length of the agents array
|
||||
};
|
||||
|
||||
@group(1) @binding(0) var<uniform> settings: Settings;
|
||||
|
||||
struct Counters {
|
||||
redAgentsAlive: atomic<u32>,
|
||||
greenAgentsAlive: atomic<u32>,
|
||||
};
|
||||
|
||||
@group(1) @binding(2) var<storage, read_write> counters: Counters;
|
||||
|
||||
|
||||
@compute @workgroup_size(64)
|
||||
fn main(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
@builtin(num_workgroups) workgroup_count: vec3<u32>
|
||||
) {
|
||||
let id = global_id.x + global_id.y * (workgroup_count.x * 64) + global_id.z * (workgroup_count.x * workgroup_count.y * 64);
|
||||
|
||||
if id >= settings.agentCount {
|
||||
return;
|
||||
}
|
||||
|
||||
if agents[id].colorIndex < 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
if agents[id].colorIndex < 0.5 {
|
||||
atomicAdd(&counters.redAgentsAlive, 1);
|
||||
} else {
|
||||
atomicAdd(&counters.greenAgentsAlive, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
@compute @workgroup_size(64)
|
||||
fn main(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
@builtin(num_workgroups) workgroup_count: vec3<u32>
|
||||
) {
|
||||
let id = get_id(global_id, workgroup_count);
|
||||
|
||||
if id >= arrayLength(&agents) {
|
||||
return;
|
||||
}
|
||||
|
||||
let clusterId = f32(id % 1000);
|
||||
|
||||
let random = textureSampleLevel(
|
||||
noise,
|
||||
noiseSampler,
|
||||
vec2(f32(id % 1999) / 2000, f32(id) / 1999 / 2000),
|
||||
0
|
||||
);
|
||||
|
||||
let randomPosition = textureSampleLevel(
|
||||
noise,
|
||||
noiseSampler,
|
||||
vec2(clusterId / 2000, clusterId / 2000),
|
||||
0
|
||||
);
|
||||
|
||||
|
||||
agents[id] = Agent(
|
||||
randomPosition.xz * state.size,
|
||||
random.r * 3.14 * 2,
|
||||
0,
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
0.0,
|
||||
0.0,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { CommonState } from '../../common-state/common-state';
|
||||
import { AGENT_SIZE_IN_BYTES } from './agent';
|
||||
import { AgentGenerationPipeline } from './agent-generation-pipeline';
|
||||
|
||||
|
|
@ -52,7 +51,7 @@ class FakeBuffer {
|
|||
private readonly mappedRange: ArrayBuffer;
|
||||
|
||||
public readonly destroy = vi.fn();
|
||||
public readonly mapAsync = vi.fn(async (_mode: number) => undefined);
|
||||
public readonly mapAsync = vi.fn(async () => undefined);
|
||||
public readonly getMappedRange = vi.fn(() => this.mappedRange);
|
||||
public readonly unmap = vi.fn();
|
||||
|
||||
|
|
@ -72,9 +71,7 @@ class FakeComputePass {
|
|||
public readonly setPipeline = vi.fn((pipeline: GPUComputePipeline) => {
|
||||
this.pipeline = pipeline as unknown as FakePipeline;
|
||||
});
|
||||
public readonly setBindGroup = vi.fn(
|
||||
(_index: number, _bindGroup: GPUBindGroup) => undefined
|
||||
);
|
||||
public readonly setBindGroup = vi.fn(() => undefined);
|
||||
public readonly dispatchWorkgroups = vi.fn((x: number, y = 1, z = 1) => {
|
||||
this.device.dispatchCalls.push({
|
||||
entryPoint: this.pipeline?.entryPoint ?? 'unset',
|
||||
|
|
@ -111,12 +108,8 @@ class FakeCommandEncoder {
|
|||
}
|
||||
|
||||
class FakeQueue {
|
||||
public readonly writeBuffer = vi.fn(
|
||||
(_buffer: GPUBuffer, _offset: number, _data: BufferSource) => undefined
|
||||
);
|
||||
public readonly submit = vi.fn(
|
||||
(_commandBuffers: Iterable<GPUCommandBuffer>) => undefined
|
||||
);
|
||||
public readonly writeBuffer = vi.fn(() => undefined);
|
||||
public readonly submit = vi.fn(() => undefined);
|
||||
}
|
||||
|
||||
class FakeShaderModule {
|
||||
|
|
@ -137,9 +130,7 @@ class FakeDevice {
|
|||
|
||||
private bufferIndex = 0;
|
||||
|
||||
public readonly createBindGroupLayout = vi.fn(
|
||||
(_descriptor: GPUBindGroupLayoutDescriptor) => ({}) as GPUBindGroupLayout
|
||||
);
|
||||
public readonly createBindGroupLayout = vi.fn(() => ({}) as GPUBindGroupLayout);
|
||||
public readonly createBuffer = vi.fn((descriptor: GPUBufferDescriptor) => {
|
||||
const label =
|
||||
['agents', 'compactedAgents', 'counters', 'countersStaging', 'uniforms'][
|
||||
|
|
@ -155,15 +146,10 @@ class FakeDevice {
|
|||
isMappedReadBuffer ? this.compactedCount : 0
|
||||
) as unknown as GPUBuffer;
|
||||
});
|
||||
public readonly createBindGroup = vi.fn(
|
||||
(_descriptor: GPUBindGroupDescriptor) => ({}) as GPUBindGroup
|
||||
);
|
||||
public readonly createPipelineLayout = vi.fn(
|
||||
(_descriptor: GPUPipelineLayoutDescriptor) => ({}) as GPUPipelineLayout
|
||||
);
|
||||
public readonly createBindGroup = vi.fn(() => ({}) as GPUBindGroup);
|
||||
public readonly createPipelineLayout = vi.fn(() => ({}) as GPUPipelineLayout);
|
||||
public readonly createShaderModule = vi.fn(
|
||||
(_descriptor: GPUShaderModuleDescriptor) =>
|
||||
new FakeShaderModule() as unknown as GPUShaderModule
|
||||
() => new FakeShaderModule() as unknown as GPUShaderModule
|
||||
);
|
||||
public readonly createComputePipeline = vi.fn(
|
||||
(descriptor: GPUComputePipelineDescriptor) => {
|
||||
|
|
@ -183,18 +169,10 @@ const createPipeline = (compactedCount: number) => {
|
|||
installGpuConstants();
|
||||
|
||||
const device = new FakeDevice(compactedCount);
|
||||
const commonState = {
|
||||
bindGroupLayout: {} as GPUBindGroupLayout,
|
||||
execute: vi.fn(),
|
||||
} as unknown as CommonState;
|
||||
|
||||
return {
|
||||
device,
|
||||
pipeline: new AgentGenerationPipeline(
|
||||
device as unknown as GPUDevice,
|
||||
commonState,
|
||||
1024
|
||||
),
|
||||
pipeline: new AgentGenerationPipeline(device as unknown as GPUDevice, 1024),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +1,29 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
import { getWorkgroupCounts } from '../../../utils/graphics/get-workgroup-counts';
|
||||
import { getWorkgroupCount } from '../../../utils/graphics/get-workgroup-count';
|
||||
import { smartCompile } from '../../../utils/graphics/smart-compile';
|
||||
import { CommonState } from '../../common-state/common-state';
|
||||
import { AGENT_SIZE_IN_BYTES } from './agent';
|
||||
import compactionShader from './agent-compaction.wgsl?raw';
|
||||
import countingShader from './agent-counting.wgsl?raw';
|
||||
import firstGenerationShader from './agent-first-generation.wgsl?raw';
|
||||
import resizeShader from './agent-resize.wgsl?raw';
|
||||
import agentSchema from './agent-schema.wgsl?raw';
|
||||
import { GenerationCounts } from './generation-counts';
|
||||
|
||||
export class AgentGenerationPipeline {
|
||||
private static readonly WORKGROUP_SIZE = 64;
|
||||
private static readonly UNIFORM_COUNT = 4;
|
||||
private static readonly COUNTER_COUNT = 3;
|
||||
private static readonly COUNTER_COUNT = 1;
|
||||
|
||||
private readonly bindGroupLayout: GPUBindGroupLayout;
|
||||
private readonly compactionBindGroupLayout: GPUBindGroupLayout;
|
||||
private readonly uniforms: GPUBuffer;
|
||||
private readonly bindGroup: GPUBindGroup;
|
||||
private readonly compactionBindGroup: GPUBindGroup;
|
||||
|
||||
private readonly firstGenerationPipeline: GPUComputePipeline;
|
||||
private readonly countingPipeline: GPUComputePipeline;
|
||||
private readonly resizePipeline: GPUComputePipeline;
|
||||
private readonly compactionPipeline: GPUComputePipeline;
|
||||
private readonly compactedAgentsCopyPipeline: GPUComputePipeline;
|
||||
|
||||
public readonly agentsBuffer: GPUBuffer;
|
||||
private readonly compactedAgentsBuffer: GPUBuffer;
|
||||
public readonly countersBuffer: GPUBuffer;
|
||||
public readonly countersStagingBuffer: GPUBuffer;
|
||||
private readonly countersBuffer: GPUBuffer;
|
||||
private readonly countersStagingBuffer: GPUBuffer;
|
||||
private readonly counterClearValues = new Uint32Array(
|
||||
AgentGenerationPipeline.COUNTER_COUNT
|
||||
);
|
||||
|
|
@ -41,36 +33,10 @@ export class AgentGenerationPipeline {
|
|||
|
||||
public constructor(
|
||||
private readonly device: GPUDevice,
|
||||
private readonly commonState: CommonState,
|
||||
private readonly maxAgentCountUpperLimit: number
|
||||
) {
|
||||
const emptyBindGroupLayout = device.createBindGroupLayout({ entries: [] });
|
||||
this.bindGroupLayout = device.createBindGroupLayout({
|
||||
entries: [
|
||||
{
|
||||
binding: 0,
|
||||
visibility: GPUShaderStage.COMPUTE,
|
||||
buffer: {
|
||||
type: 'uniform',
|
||||
},
|
||||
},
|
||||
{
|
||||
binding: 1,
|
||||
visibility: GPUShaderStage.COMPUTE,
|
||||
buffer: {
|
||||
type: 'storage',
|
||||
},
|
||||
},
|
||||
{
|
||||
binding: 2,
|
||||
visibility: GPUShaderStage.COMPUTE,
|
||||
buffer: {
|
||||
type: 'storage',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
this.compactionBindGroupLayout = device.createBindGroupLayout({
|
||||
entries: [
|
||||
{
|
||||
binding: 0,
|
||||
|
|
@ -130,30 +96,6 @@ export class AgentGenerationPipeline {
|
|||
|
||||
this.bindGroup = this.device.createBindGroup({
|
||||
layout: this.bindGroupLayout,
|
||||
entries: [
|
||||
{
|
||||
binding: 0,
|
||||
resource: {
|
||||
buffer: this.uniforms,
|
||||
},
|
||||
},
|
||||
{
|
||||
binding: 1,
|
||||
resource: {
|
||||
buffer: this.agentsBuffer,
|
||||
},
|
||||
},
|
||||
{
|
||||
binding: 2,
|
||||
resource: {
|
||||
buffer: this.countersBuffer,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
this.compactionBindGroup = this.device.createBindGroup({
|
||||
layout: this.compactionBindGroupLayout,
|
||||
entries: [
|
||||
{
|
||||
binding: 0,
|
||||
|
|
@ -182,51 +124,21 @@ export class AgentGenerationPipeline {
|
|||
],
|
||||
});
|
||||
|
||||
this.firstGenerationPipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [commonState.bindGroupLayout, this.bindGroupLayout],
|
||||
}),
|
||||
compute: {
|
||||
module: smartCompile(
|
||||
device,
|
||||
CommonState.shaderCode,
|
||||
agentSchema,
|
||||
firstGenerationShader
|
||||
),
|
||||
entryPoint: 'main',
|
||||
},
|
||||
});
|
||||
|
||||
this.countingPipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [commonState.bindGroupLayout, this.bindGroupLayout],
|
||||
}),
|
||||
compute: {
|
||||
module: smartCompile(device, CommonState.shaderCode, agentSchema, countingShader),
|
||||
entryPoint: 'main',
|
||||
},
|
||||
});
|
||||
|
||||
this.resizePipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [commonState.bindGroupLayout, this.bindGroupLayout],
|
||||
bindGroupLayouts: [emptyBindGroupLayout, this.bindGroupLayout],
|
||||
}),
|
||||
compute: {
|
||||
module: smartCompile(device, CommonState.shaderCode, agentSchema, resizeShader),
|
||||
module: smartCompile(device, agentSchema, resizeShader),
|
||||
entryPoint: 'main',
|
||||
},
|
||||
});
|
||||
|
||||
const compactionModule = smartCompile(
|
||||
device,
|
||||
CommonState.shaderCode,
|
||||
agentSchema,
|
||||
compactionShader
|
||||
);
|
||||
const compactionModule = smartCompile(device, agentSchema, compactionShader);
|
||||
|
||||
this.compactionPipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [commonState.bindGroupLayout, this.compactionBindGroupLayout],
|
||||
bindGroupLayouts: [emptyBindGroupLayout, this.bindGroupLayout],
|
||||
}),
|
||||
compute: {
|
||||
module: compactionModule,
|
||||
|
|
@ -236,7 +148,7 @@ export class AgentGenerationPipeline {
|
|||
|
||||
this.compactedAgentsCopyPipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [commonState.bindGroupLayout, this.compactionBindGroupLayout],
|
||||
bindGroupLayouts: [emptyBindGroupLayout, this.bindGroupLayout],
|
||||
}),
|
||||
compute: {
|
||||
module: compactionModule,
|
||||
|
|
@ -251,7 +163,8 @@ export class AgentGenerationPipeline {
|
|||
? this.maxAgentCountUpperLimit
|
||||
: Number.POSITIVE_INFINITY,
|
||||
Math.floor(this.device.limits.maxBufferSize / AGENT_SIZE_IN_BYTES) - 1,
|
||||
this.device.limits.maxComputeWorkgroupsPerDimension ** 3
|
||||
this.device.limits.maxComputeWorkgroupsPerDimension *
|
||||
AgentGenerationPipeline.WORKGROUP_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -276,82 +189,16 @@ export class AgentGenerationPipeline {
|
|||
|
||||
const commandEncoder = this.device.createCommandEncoder();
|
||||
const passEncoder = commandEncoder.beginComputePass();
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setPipeline(this.resizePipeline);
|
||||
passEncoder.setBindGroup(1, this.bindGroup);
|
||||
passEncoder.dispatchWorkgroups(
|
||||
...getWorkgroupCounts(
|
||||
this.device,
|
||||
agentCount,
|
||||
AgentGenerationPipeline.WORKGROUP_SIZE
|
||||
)
|
||||
getWorkgroupCount(agentCount, AgentGenerationPipeline.WORKGROUP_SIZE)
|
||||
);
|
||||
passEncoder.end();
|
||||
|
||||
this.device.queue.submit([commandEncoder.finish()]);
|
||||
}
|
||||
|
||||
public spawnFirstGeneration(): void {
|
||||
const commandEncoder = this.device.createCommandEncoder();
|
||||
|
||||
const passEncoder = commandEncoder.beginComputePass();
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setPipeline(this.firstGenerationPipeline);
|
||||
passEncoder.setBindGroup(1, this.bindGroup);
|
||||
passEncoder.dispatchWorkgroups(
|
||||
...getWorkgroupCounts(
|
||||
this.device,
|
||||
this.maxAgentCount,
|
||||
AgentGenerationPipeline.WORKGROUP_SIZE
|
||||
)
|
||||
);
|
||||
passEncoder.end();
|
||||
|
||||
this.device.queue.submit([commandEncoder.finish()]);
|
||||
}
|
||||
|
||||
public async countAgents(agentCount: number): Promise<GenerationCounts> {
|
||||
this.counterClearValues.fill(0);
|
||||
this.agentCountUniformValues.fill(0);
|
||||
this.agentCountUniformValues[0] = agentCount;
|
||||
this.device.queue.writeBuffer(this.countersBuffer, 0, this.counterClearValues);
|
||||
this.device.queue.writeBuffer(this.uniforms, 0, this.agentCountUniformValues);
|
||||
|
||||
const commandEncoder = this.device.createCommandEncoder();
|
||||
|
||||
const passEncoder = commandEncoder.beginComputePass();
|
||||
passEncoder.setPipeline(this.countingPipeline);
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setBindGroup(1, this.bindGroup);
|
||||
passEncoder.dispatchWorkgroups(
|
||||
...getWorkgroupCounts(
|
||||
this.device,
|
||||
agentCount,
|
||||
AgentGenerationPipeline.WORKGROUP_SIZE
|
||||
)
|
||||
);
|
||||
passEncoder.end();
|
||||
|
||||
commandEncoder.copyBufferToBuffer(
|
||||
this.countersBuffer,
|
||||
0,
|
||||
this.countersStagingBuffer,
|
||||
0,
|
||||
AgentGenerationPipeline.COUNTER_COUNT * Uint32Array.BYTES_PER_ELEMENT
|
||||
);
|
||||
|
||||
this.device.queue.submit([commandEncoder.finish()]);
|
||||
|
||||
await this.countersStagingBuffer.mapAsync(GPUMapMode.READ);
|
||||
|
||||
const data = new Uint32Array(this.countersStagingBuffer.getMappedRange().slice(0));
|
||||
this.countersStagingBuffer.unmap();
|
||||
return {
|
||||
evenGenerationCount: data[0],
|
||||
oddGenerationCount: data[1],
|
||||
};
|
||||
}
|
||||
|
||||
public async compactAgents(agentCount: number): Promise<number> {
|
||||
if (agentCount <= 0) {
|
||||
return 0;
|
||||
|
|
@ -366,27 +213,17 @@ export class AgentGenerationPipeline {
|
|||
const commandEncoder = this.device.createCommandEncoder();
|
||||
const passEncoder = commandEncoder.beginComputePass();
|
||||
passEncoder.setPipeline(this.compactionPipeline);
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setBindGroup(1, this.compactionBindGroup);
|
||||
passEncoder.setBindGroup(1, this.bindGroup);
|
||||
passEncoder.dispatchWorkgroups(
|
||||
...getWorkgroupCounts(
|
||||
this.device,
|
||||
agentCount,
|
||||
AgentGenerationPipeline.WORKGROUP_SIZE
|
||||
)
|
||||
getWorkgroupCount(agentCount, AgentGenerationPipeline.WORKGROUP_SIZE)
|
||||
);
|
||||
passEncoder.end();
|
||||
|
||||
const copyPassEncoder = commandEncoder.beginComputePass();
|
||||
copyPassEncoder.setPipeline(this.compactedAgentsCopyPipeline);
|
||||
this.commonState.execute(copyPassEncoder);
|
||||
copyPassEncoder.setBindGroup(1, this.compactionBindGroup);
|
||||
copyPassEncoder.setBindGroup(1, this.bindGroup);
|
||||
copyPassEncoder.dispatchWorkgroups(
|
||||
...getWorkgroupCounts(
|
||||
this.device,
|
||||
agentCount,
|
||||
AgentGenerationPipeline.WORKGROUP_SIZE
|
||||
)
|
||||
getWorkgroupCount(agentCount, AgentGenerationPipeline.WORKGROUP_SIZE)
|
||||
);
|
||||
copyPassEncoder.end();
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@ struct ResizeSettings {
|
|||
|
||||
@compute @workgroup_size(64)
|
||||
fn main(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
@builtin(num_workgroups) workgroup_count: vec3<u32>
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>
|
||||
) {
|
||||
let id = get_id(global_id, workgroup_count);
|
||||
let id = get_id(global_id);
|
||||
|
||||
if id >= u32(resizeSettings.agentCount) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import { describe, expect, it } from 'vitest';
|
|||
|
||||
import { AGENT_FLOAT_COUNT, AGENT_SIZE_IN_BYTES } from './agent';
|
||||
import compactionShader from './agent-compaction.wgsl?raw';
|
||||
import countingShader from './agent-counting.wgsl?raw';
|
||||
import firstGenerationShader from './agent-first-generation.wgsl?raw';
|
||||
import resizeShader from './agent-resize.wgsl?raw';
|
||||
import agentSchema from './agent-schema.wgsl?raw';
|
||||
|
||||
|
|
@ -60,15 +58,12 @@ describe('Agent TS/WGSL contract', () => {
|
|||
});
|
||||
|
||||
it('keeps generation shader workgroup sizes aligned with agent indexing', () => {
|
||||
[firstGenerationShader, countingShader, resizeShader, compactionShader].forEach(
|
||||
(shader) => {
|
||||
expect(shader).toMatch(/@workgroup_size\(64\)/);
|
||||
}
|
||||
);
|
||||
[resizeShader, compactionShader].forEach((shader) => {
|
||||
expect(shader).toMatch(/@workgroup_size\(64\)/);
|
||||
});
|
||||
|
||||
expect(agentSchema).toContain('workgroup_count.x * 64');
|
||||
expect(agentSchema).toContain('workgroup_count.x * workgroup_count.y * 64');
|
||||
expect(compactionShader).toContain('let id = get_id(global_id, workgroup_count);');
|
||||
expect(agentSchema).toContain('return global_id.x;');
|
||||
expect(compactionShader).toContain('let id = get_id(global_id);');
|
||||
expect(compactionShader).toContain('if id < settings.agentCount');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ struct Agent {
|
|||
|
||||
@group(1) @binding(1) var<storage, read_write> agents: array<Agent>;
|
||||
|
||||
fn get_id(global_id: vec3<u32>, workgroup_count: vec3<u32>) -> u32 {
|
||||
return global_id.x + global_id.y * (workgroup_count.x * 64) + global_id.z * (workgroup_count.x * workgroup_count.y * 64);
|
||||
}
|
||||
fn get_id(global_id: vec3<u32>) -> u32 {
|
||||
return global_id.x;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
export interface GenerationCounts {
|
||||
evenGenerationCount: number;
|
||||
oddGenerationCount: number;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ import {
|
|||
createCachedFloat32BufferWrite,
|
||||
writeFloat32BufferIfChanged,
|
||||
} from '../../utils/graphics/cached-buffer-write';
|
||||
import { getWorkgroupCounts } from '../../utils/graphics/get-workgroup-counts';
|
||||
import { getWorkgroupCount } from '../../utils/graphics/get-workgroup-count';
|
||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||
import { CommonState } from '../common-state/common-state';
|
||||
import agentSchema from './agent-generation/agent-schema.wgsl?raw';
|
||||
|
|
@ -117,7 +117,7 @@ export class AgentPipeline {
|
|||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setBindGroup(1, bindGroup);
|
||||
passEncoder.dispatchWorkgroups(
|
||||
...getWorkgroupCounts(this.device, this.agentCount, AgentPipeline.WORKGROUP_SIZE)
|
||||
getWorkgroupCount(this.agentCount, AgentPipeline.WORKGROUP_SIZE)
|
||||
);
|
||||
passEncoder.end();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,10 +25,9 @@ struct Settings {
|
|||
|
||||
@compute @workgroup_size(64)
|
||||
fn main(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
@builtin(num_workgroups) workgroup_count: vec3<u32>
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>
|
||||
) {
|
||||
let id = get_id(global_id, workgroup_count);
|
||||
let id = get_id(global_id);
|
||||
|
||||
if id >= u32(settings.agentCount) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
import { appConfig } from '../../config';
|
||||
import { clamp } from '../../utils/clamp';
|
||||
import {
|
||||
createCachedFloat32BufferWrite,
|
||||
writeFloat32BufferIfChanged,
|
||||
|
|
@ -25,7 +24,6 @@ export class BrushPipeline {
|
|||
|
||||
private readonly bindGroupLayout: GPUBindGroupLayout;
|
||||
private readonly bindGroup: GPUBindGroup;
|
||||
private readonly pipeline: GPURenderPipeline;
|
||||
private readonly multiTargetPipeline: GPURenderPipeline;
|
||||
private readonly uniforms: GPUBuffer;
|
||||
private readonly uniformValues = new Float32Array(BrushPipeline.UNIFORM_COUNT);
|
||||
|
|
@ -39,7 +37,6 @@ export class BrushPipeline {
|
|||
BrushPipeline.ATTRIBUTES_PER_LINE_SEGMENT
|
||||
);
|
||||
|
||||
private linePoints: Array<vec2> = [];
|
||||
private lineSegments: Array<LineSegment> = [];
|
||||
private actualSegments: Array<LineSegment> = [];
|
||||
|
||||
|
|
@ -59,7 +56,6 @@ export class BrushPipeline {
|
|||
});
|
||||
|
||||
const shaderModule = smartCompile(device, CommonState.shaderCode, shader);
|
||||
this.pipeline = this.createPipeline(shaderModule, 'fragment', 1);
|
||||
this.multiTargetPipeline = this.createPipeline(shaderModule, 'fragmentMrt', 2);
|
||||
|
||||
this.uniforms = this.device.createBuffer({
|
||||
|
|
@ -80,12 +76,6 @@ export class BrushPipeline {
|
|||
});
|
||||
}
|
||||
|
||||
public addSwipe(position: vec2) {
|
||||
const previousPosition = this.linePoints[this.linePoints.length - 1] ?? position;
|
||||
this.addSwipeSegment(previousPosition, position);
|
||||
this.linePoints.push(vec2.clone(position));
|
||||
}
|
||||
|
||||
public addSwipeSegment(from: vec2, to: vec2) {
|
||||
this.lineSegments.push({
|
||||
from: vec2.clone(from),
|
||||
|
|
@ -94,7 +84,6 @@ export class BrushPipeline {
|
|||
}
|
||||
|
||||
public clearSwipes() {
|
||||
this.linePoints.length = 0;
|
||||
this.lineSegments.length = 0;
|
||||
this.actualSegments.length = 0;
|
||||
}
|
||||
|
|
@ -103,8 +92,7 @@ export class BrushPipeline {
|
|||
brushSize,
|
||||
brushSizeVariation,
|
||||
selectedColorIndex,
|
||||
isErasing,
|
||||
}: BrushSettings & { selectedColorIndex: number; isErasing: boolean }) {
|
||||
}: BrushSettings & { selectedColorIndex: number }) {
|
||||
const brushRadius = brushSize / 2;
|
||||
const brushRadiusVariation = Math.floor(brushRadius * brushSizeVariation);
|
||||
const brushFeather = Math.max(1, brushRadius * BrushPipeline.FEATHER_RADIUS_RATIO);
|
||||
|
|
@ -115,10 +103,10 @@ export class BrushPipeline {
|
|||
this.uniformValues[1] = brushRadiusVariation;
|
||||
this.uniformValues[2] = 0;
|
||||
this.uniformValues[3] = 0;
|
||||
this.uniformValues[4] = !isErasing && selectedColorIndex === 0 ? 1 : 0;
|
||||
this.uniformValues[5] = !isErasing && selectedColorIndex === 1 ? 1 : 0;
|
||||
this.uniformValues[6] = !isErasing && selectedColorIndex === 2 ? 1 : 0;
|
||||
this.uniformValues[7] = isErasing ? 0 : 1;
|
||||
this.uniformValues[4] = selectedColorIndex === 0 ? 1 : 0;
|
||||
this.uniformValues[5] = selectedColorIndex === 1 ? 1 : 0;
|
||||
this.uniformValues[6] = selectedColorIndex === 2 ? 1 : 0;
|
||||
this.uniformValues[7] = 1;
|
||||
writeFloat32BufferIfChanged(
|
||||
this.device,
|
||||
this.uniforms,
|
||||
|
|
@ -264,10 +252,6 @@ export class BrushPipeline {
|
|||
return offset;
|
||||
}
|
||||
|
||||
public execute(commandEncoder: GPUCommandEncoder, trailMapOut: GPUTextureView): void {
|
||||
this.executeWithPipeline(commandEncoder, this.pipeline, [trailMapOut]);
|
||||
}
|
||||
|
||||
public executeMultiTarget(
|
||||
commandEncoder: GPUCommandEncoder,
|
||||
sourceMapOut: GPUTextureView,
|
||||
|
|
@ -392,6 +376,6 @@ export class BrushPipeline {
|
|||
}
|
||||
|
||||
private get lineCount() {
|
||||
return clamp(this.actualSegments.length, 0, BrushPipeline.MAX_LINE_COUNT);
|
||||
return this.actualSegments.length;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
export interface BrushSettings {
|
||||
brushSize: number;
|
||||
brushCurveResolution: number;
|
||||
eraserSize: number;
|
||||
mirrorSegmentCount: number;
|
||||
brushSizeVariation: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,21 +31,6 @@ fn vertex(
|
|||
return VertexOutput(vec4(position, 0.0, 1.0), screenPosition, start, end);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(
|
||||
@location(0) screenPosition: vec2<f32>,
|
||||
@location(1) start: vec2<f32>,
|
||||
@location(2) end: vec2<f32>
|
||||
) -> @location(0) vec4<f32> {
|
||||
let strength = brushStrength(screenPosition, start, end);
|
||||
|
||||
if(strength < 0.02) {
|
||||
discard;
|
||||
}
|
||||
|
||||
return brushOutput(strength);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragmentMrt(
|
||||
@location(0) screenPosition: vec2<f32>,
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ export class CommonState {
|
|||
public static readonly shaderCode = /* wgsl */ `
|
||||
struct State {
|
||||
size: vec2<f32>,
|
||||
deltaTime: f32,
|
||||
time: f32,
|
||||
padding0: f32,
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> state: State;
|
||||
|
|
@ -95,19 +95,11 @@ export class CommonState {
|
|||
});
|
||||
}
|
||||
|
||||
public setParameters({
|
||||
canvasSize,
|
||||
deltaTime,
|
||||
time,
|
||||
}: {
|
||||
canvasSize: vec2;
|
||||
deltaTime: number;
|
||||
time: number;
|
||||
}) {
|
||||
public setParameters({ canvasSize, time }: { canvasSize: vec2; time: number }) {
|
||||
this.uniformValues[0] = canvasSize[0];
|
||||
this.uniformValues[1] = canvasSize[1];
|
||||
this.uniformValues[2] = deltaTime;
|
||||
this.uniformValues[3] = time;
|
||||
this.uniformValues[2] = time;
|
||||
this.uniformValues[3] = 0;
|
||||
writeFloat32BufferIfChanged(
|
||||
this.device,
|
||||
this.uniforms,
|
||||
|
|
|
|||
|
|
@ -53,12 +53,13 @@ export class CopyPipeline {
|
|||
new Float32Array(this.vertexBuffer.getMappedRange()).set(vertexData);
|
||||
this.vertexBuffer.unmap();
|
||||
|
||||
const shaderModule = smartCompile(device, shader);
|
||||
this.pipeline = device.createRenderPipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [this.bindGroupLayout],
|
||||
}),
|
||||
vertex: {
|
||||
module: smartCompile(device, shader),
|
||||
module: shaderModule,
|
||||
entryPoint: 'vertex',
|
||||
buffers: [
|
||||
{
|
||||
|
|
@ -75,7 +76,7 @@ export class CopyPipeline {
|
|||
],
|
||||
},
|
||||
fragment: {
|
||||
module: smartCompile(device, shader),
|
||||
module: shaderModule,
|
||||
entryPoint: 'fragment',
|
||||
targets: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ struct Settings {
|
|||
};
|
||||
|
||||
|
||||
@group(1) @binding(0) var<uniform> settings: Settings;
|
||||
@group(1) @binding(2) var trailMap: texture_2d<f32>;
|
||||
@group(0) @binding(0) var<uniform> settings: Settings;
|
||||
@group(0) @binding(2) var trailMap: texture_2d<f32>;
|
||||
|
||||
|
||||
@fragment
|
||||
|
|
|
|||
|
|
@ -34,4 +34,10 @@ describe('diffusion pipeline parameters', () => {
|
|||
expect(shader).not.toContain('pow(');
|
||||
expect(shader).not.toContain('noise');
|
||||
});
|
||||
|
||||
it('keeps shader resource groups aligned with the simplified pipeline layout', () => {
|
||||
expect(shader).toContain('@group(0) @binding(0) var<uniform> settings');
|
||||
expect(shader).toContain('@group(0) @binding(2) var trailMap');
|
||||
expect(shader).not.toContain('@group(1)');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import {
|
|||
} from '../../utils/graphics/cached-buffer-write';
|
||||
import { setUpFullScreenQuad } from '../../utils/graphics/full-screen-quad';
|
||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||
import { CommonState } from '../common-state/common-state';
|
||||
import shader from './diffuse.wgsl?raw';
|
||||
import { DiffusionSettings } from './diffusion-settings';
|
||||
|
||||
|
|
@ -51,10 +50,7 @@ export class DiffusionPipeline {
|
|||
|
||||
private readonly bindGroupsByInput = new WeakMap<GPUTextureView, GPUBindGroup>();
|
||||
|
||||
public constructor(
|
||||
private readonly device: GPUDevice,
|
||||
private readonly commonState: CommonState
|
||||
) {
|
||||
public constructor(private readonly device: GPUDevice) {
|
||||
this.bindGroupLayout = device.createBindGroupLayout(
|
||||
DiffusionPipeline.bindGroupLayout
|
||||
);
|
||||
|
|
@ -64,11 +60,11 @@ export class DiffusionPipeline {
|
|||
|
||||
this.pipeline = device.createRenderPipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [commonState.bindGroupLayout, this.bindGroupLayout],
|
||||
bindGroupLayouts: [this.bindGroupLayout],
|
||||
}),
|
||||
vertex,
|
||||
fragment: {
|
||||
module: smartCompile(device, CommonState.shaderCode, shader),
|
||||
module: smartCompile(device, shader),
|
||||
entryPoint: 'fragment',
|
||||
targets: [
|
||||
{
|
||||
|
|
@ -128,8 +124,7 @@ export class DiffusionPipeline {
|
|||
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
||||
passEncoder.setPipeline(this.pipeline);
|
||||
passEncoder.setVertexBuffer(0, this.vertexBuffer);
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setBindGroup(1, bindGroup);
|
||||
passEncoder.setBindGroup(0, bindGroup);
|
||||
passEncoder.draw(4, 1);
|
||||
passEncoder.end();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,4 @@ export interface DiffusionSettings {
|
|||
decayRateTrails: number;
|
||||
diffusionRateBrush: number;
|
||||
decayRateBrush: number;
|
||||
brushEffectDuration: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
import { appConfig } from '../../config';
|
||||
import {
|
||||
createCachedFloat32BufferWrite,
|
||||
writeFloat32BufferIfChanged,
|
||||
} from '../../utils/graphics/cached-buffer-write';
|
||||
import { getWorkgroupCounts } from '../../utils/graphics/get-workgroup-counts';
|
||||
import { getWorkgroupCount } from '../../utils/graphics/get-workgroup-count';
|
||||
import { smartCompile } from '../../utils/graphics/smart-compile';
|
||||
import agentSchema from '../agents/agent-generation/agent-schema.wgsl?raw';
|
||||
import { CommonState } from '../common-state/common-state';
|
||||
import shader from './eraser-agent.wgsl?raw';
|
||||
|
||||
export class EraserAgentPipeline {
|
||||
private static readonly WORKGROUP_SIZE = appConfig.pipelines.eraser.workgroupSize;
|
||||
private static readonly WORKGROUP_SIZE = 64;
|
||||
private static readonly UNIFORM_COUNT = 4;
|
||||
|
||||
private readonly bindGroupLayout: GPUBindGroupLayout;
|
||||
|
|
@ -24,16 +20,15 @@ export class EraserAgentPipeline {
|
|||
);
|
||||
private readonly bindGroupsByMask = new WeakMap<GPUTextureView, GPUBindGroup>();
|
||||
|
||||
private linePoints: Array<vec2> = [];
|
||||
private pendingSegmentCount = 0;
|
||||
private activeSegmentCount = 0;
|
||||
private agentCount = 0;
|
||||
|
||||
public constructor(
|
||||
private readonly device: GPUDevice,
|
||||
private readonly commonState: CommonState,
|
||||
private readonly agentsBuffer: GPUBuffer
|
||||
) {
|
||||
const emptyBindGroupLayout = device.createBindGroupLayout({ entries: [] });
|
||||
this.bindGroupLayout = device.createBindGroupLayout({
|
||||
entries: [
|
||||
{
|
||||
|
|
@ -67,41 +62,25 @@ export class EraserAgentPipeline {
|
|||
|
||||
this.pipeline = device.createComputePipeline({
|
||||
layout: device.createPipelineLayout({
|
||||
bindGroupLayouts: [commonState.bindGroupLayout, this.bindGroupLayout],
|
||||
bindGroupLayouts: [emptyBindGroupLayout, this.bindGroupLayout],
|
||||
}),
|
||||
compute: {
|
||||
module: smartCompile(device, CommonState.shaderCode, agentSchema, shader),
|
||||
module: smartCompile(device, agentSchema, shader),
|
||||
entryPoint: 'main',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public addSwipe(position: vec2): void {
|
||||
const previousPosition = this.linePoints[this.linePoints.length - 1] ?? position;
|
||||
this.addSwipeSegment(previousPosition, position);
|
||||
this.linePoints.push(vec2.clone(position));
|
||||
}
|
||||
|
||||
public addSwipeSegment(from: vec2, to: vec2): void {
|
||||
void from;
|
||||
void to;
|
||||
public addSwipeSegment(): void {
|
||||
this.pendingSegmentCount += 1;
|
||||
}
|
||||
|
||||
public clearSwipes(): void {
|
||||
this.linePoints.length = 0;
|
||||
this.pendingSegmentCount = 0;
|
||||
this.activeSegmentCount = 0;
|
||||
}
|
||||
|
||||
public setParameters({
|
||||
agentCount,
|
||||
eraserSize: _eraserSize,
|
||||
}: {
|
||||
agentCount: number;
|
||||
eraserSize: number;
|
||||
}): void {
|
||||
void _eraserSize;
|
||||
public setParameters({ agentCount }: { agentCount: number }): void {
|
||||
this.agentCount = agentCount;
|
||||
this.activeSegmentCount = this.pendingSegmentCount;
|
||||
this.pendingSegmentCount = 0;
|
||||
|
|
@ -129,14 +108,9 @@ export class EraserAgentPipeline {
|
|||
|
||||
const passEncoder = commandEncoder.beginComputePass();
|
||||
passEncoder.setPipeline(this.pipeline);
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setBindGroup(1, this.getBindGroup(eraserMask));
|
||||
passEncoder.dispatchWorkgroups(
|
||||
...getWorkgroupCounts(
|
||||
this.device,
|
||||
this.agentCount,
|
||||
EraserAgentPipeline.WORKGROUP_SIZE
|
||||
)
|
||||
getWorkgroupCount(this.agentCount, EraserAgentPipeline.WORKGROUP_SIZE)
|
||||
);
|
||||
passEncoder.end();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,9 @@ struct Settings {
|
|||
|
||||
@compute @workgroup_size(64)
|
||||
fn main(
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>,
|
||||
@builtin(num_workgroups) workgroup_count: vec3<u32>
|
||||
@builtin(global_invocation_id) global_id: vec3<u32>
|
||||
) {
|
||||
let id = get_id(global_id, workgroup_count);
|
||||
let id = get_id(global_id);
|
||||
|
||||
if id >= u32(settings.agentCount) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
import { appConfig } from '../../config';
|
||||
import { clamp } from '../../utils/clamp';
|
||||
import {
|
||||
createCachedFloat32BufferWrite,
|
||||
writeFloat32BufferIfChanged,
|
||||
|
|
@ -37,7 +36,6 @@ export class EraserTexturePipeline {
|
|||
EraserTexturePipeline.ATTRIBUTES_PER_LINE_SEGMENT
|
||||
);
|
||||
|
||||
private linePoints: Array<vec2> = [];
|
||||
private lineSegments: Array<LineSegment> = [];
|
||||
private actualSegments: Array<LineSegment> = [];
|
||||
|
||||
|
|
@ -88,12 +86,6 @@ export class EraserTexturePipeline {
|
|||
});
|
||||
}
|
||||
|
||||
public addSwipe(position: vec2): void {
|
||||
const previousPosition = this.linePoints[this.linePoints.length - 1] ?? position;
|
||||
this.addSwipeSegment(previousPosition, position);
|
||||
this.linePoints.push(vec2.clone(position));
|
||||
}
|
||||
|
||||
public addSwipeSegment(from: vec2, to: vec2): void {
|
||||
this.lineSegments.push({
|
||||
from: vec2.clone(from),
|
||||
|
|
@ -102,7 +94,6 @@ export class EraserTexturePipeline {
|
|||
}
|
||||
|
||||
public clearSwipes(): void {
|
||||
this.linePoints.length = 0;
|
||||
this.lineSegments.length = 0;
|
||||
this.actualSegments.length = 0;
|
||||
}
|
||||
|
|
@ -110,8 +101,8 @@ export class EraserTexturePipeline {
|
|||
public setParameters({ eraserSize }: { eraserSize: number }): void {
|
||||
const eraserRadius = eraserSize / 2;
|
||||
|
||||
this.uniformValues[0] = eraserRadius;
|
||||
this.uniformValues[1] = eraserRadius * eraserRadius;
|
||||
this.uniformValues[0] = eraserRadius * eraserRadius;
|
||||
this.uniformValues[1] = 0;
|
||||
this.uniformValues[2] = 0;
|
||||
this.uniformValues[3] = 0;
|
||||
writeFloat32BufferIfChanged(
|
||||
|
|
@ -356,6 +347,6 @@ export class EraserTexturePipeline {
|
|||
}
|
||||
|
||||
private get lineCount(): number {
|
||||
return clamp(this.actualSegments.length, 0, EraserTexturePipeline.MAX_LINE_COUNT);
|
||||
return this.actualSegments.length;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
struct Settings {
|
||||
eraserRadius: f32,
|
||||
eraserRadiusSquared: f32,
|
||||
padding0: f32,
|
||||
padding1: f32,
|
||||
padding2: f32,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ import { RenderSettings } from './render-settings';
|
|||
import shader from './render.wgsl?raw';
|
||||
|
||||
export class RenderPipeline {
|
||||
private static readonly UNIFORM_COUNT = 20;
|
||||
private static readonly UNIFORM_COUNT = 16;
|
||||
|
||||
private readonly bindGroupLayout: GPUBindGroupLayout;
|
||||
private readonly canvasPipeline: GPURenderPipeline;
|
||||
private readonly exportPipeline: GPURenderPipeline;
|
||||
private readonly pipeline: GPURenderPipeline;
|
||||
private readonly sampler: GPUSampler;
|
||||
private readonly uniforms: GPUBuffer;
|
||||
private readonly uniformValues = new Float32Array(RenderPipeline.UNIFORM_COUNT);
|
||||
|
|
@ -43,8 +42,7 @@ export class RenderPipeline {
|
|||
});
|
||||
|
||||
const format = navigator.gpu.getPreferredCanvasFormat();
|
||||
this.canvasPipeline = this.createPipeline(format, vertex);
|
||||
this.exportPipeline = this.createPipeline(format, vertex);
|
||||
this.pipeline = this.createPipeline(format, vertex);
|
||||
|
||||
this.uniforms = this.device.createBuffer({
|
||||
size: RenderPipeline.UNIFORM_COUNT * Float32Array.BYTES_PER_ELEMENT,
|
||||
|
|
@ -79,14 +77,10 @@ export class RenderPipeline {
|
|||
public setParameters({
|
||||
channelColors,
|
||||
backgroundColor,
|
||||
cameraCenter,
|
||||
cameraZoom,
|
||||
clarity,
|
||||
}: RenderSettings & {
|
||||
channelColors: Array<[number, number, number]>;
|
||||
backgroundColor: [number, number, number];
|
||||
cameraCenter: [number, number];
|
||||
cameraZoom: number;
|
||||
}) {
|
||||
const [a, b, c] = channelColors;
|
||||
this.uniformValues[0] = a[0];
|
||||
|
|
@ -105,10 +99,6 @@ export class RenderPipeline {
|
|||
this.uniformValues[13] = backgroundColor[1];
|
||||
this.uniformValues[14] = backgroundColor[2];
|
||||
this.uniformValues[15] = clarity;
|
||||
this.uniformValues[16] = cameraCenter[0];
|
||||
this.uniformValues[17] = cameraCenter[1];
|
||||
this.uniformValues[18] = cameraZoom;
|
||||
this.uniformValues[19] = 0;
|
||||
writeFloat32BufferIfChanged(
|
||||
this.device,
|
||||
this.uniforms,
|
||||
|
|
@ -136,7 +126,7 @@ export class RenderPipeline {
|
|||
],
|
||||
};
|
||||
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
||||
passEncoder.setPipeline(this.canvasPipeline);
|
||||
passEncoder.setPipeline(this.pipeline);
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setVertexBuffer(0, this.vertexBuffer);
|
||||
passEncoder.setBindGroup(1, bindGroup);
|
||||
|
|
@ -164,7 +154,7 @@ export class RenderPipeline {
|
|||
},
|
||||
],
|
||||
});
|
||||
passEncoder.setPipeline(this.exportPipeline);
|
||||
passEncoder.setPipeline(this.pipeline);
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setVertexBuffer(0, this.vertexBuffer);
|
||||
passEncoder.setBindGroup(1, bindGroup);
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ struct Settings {
|
|||
backgroundColorPadding2: f32,
|
||||
backgroundColor: vec3<f32>,
|
||||
clarity: f32,
|
||||
cameraCenter: vec2<f32>,
|
||||
cameraZoom: f32,
|
||||
padding0: f32,
|
||||
};
|
||||
|
||||
@group(1) @binding(0) var<uniform> settings: Settings;
|
||||
|
|
@ -19,10 +16,8 @@ struct Settings {
|
|||
|
||||
@fragment
|
||||
fn fragment(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
|
||||
let cameraUv = settings.cameraCenter / state.size;
|
||||
let viewUv = (uv - vec2(0.5)) / settings.cameraZoom + cameraUv;
|
||||
let traces = textureSample(trailMap, Sampler, viewUv);
|
||||
let sources = textureSample(sourceMap, Sampler, viewUv);
|
||||
let traces = textureSample(trailMap, Sampler, uv);
|
||||
let sources = textureSample(sourceMap, Sampler, uv);
|
||||
|
||||
let traceStrengths = vec3(
|
||||
clarity(traces.r),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import compactionShader from './agents/agent-generation/agent-compaction.wgsl?raw';
|
||||
import countingShader from './agents/agent-generation/agent-counting.wgsl?raw';
|
||||
import { AgentGenerationPipeline } from './agents/agent-generation/agent-generation-pipeline';
|
||||
import resizeShader from './agents/agent-generation/agent-resize.wgsl?raw';
|
||||
import { AgentPipeline } from './agents/agent-pipeline';
|
||||
|
|
@ -92,7 +91,7 @@ describe('WGSL uniform layout contracts', () => {
|
|||
pipeline: CommonState,
|
||||
source: CommonState.shaderCode,
|
||||
structName: 'State',
|
||||
fieldNames: ['size', 'deltaTime', 'time'],
|
||||
fieldNames: ['size', 'time', 'padding0'],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -157,9 +156,6 @@ describe('WGSL uniform layout contracts', () => {
|
|||
'backgroundColorPadding2',
|
||||
'backgroundColor',
|
||||
'clarity',
|
||||
'cameraCenter',
|
||||
'cameraZoom',
|
||||
'padding0',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
|
@ -175,7 +171,7 @@ describe('WGSL uniform layout contracts', () => {
|
|||
pipeline: EraserTexturePipeline,
|
||||
source: eraserTextureShader,
|
||||
structName: 'Settings',
|
||||
fieldNames: ['eraserRadius', 'eraserRadiusSquared', 'padding1', 'padding2'],
|
||||
fieldNames: ['eraserRadiusSquared', 'padding0', 'padding1', 'padding2'],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -190,7 +186,6 @@ describe('WGSL uniform layout contracts', () => {
|
|||
|
||||
it('keeps agent-generation uniforms large enough for every generation shader', () => {
|
||||
const generationUniformCounts = [
|
||||
countUniformScalars(countingShader, 'Settings'),
|
||||
countUniformScalars(resizeShader, 'ResizeSettings'),
|
||||
countUniformScalars(compactionShader, 'Settings'),
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in a new issue