This commit is contained in:
Andras Schmelczer 2026-05-16 15:05:35 +01:00
parent 70423851ba
commit 1fe5015056
55 changed files with 2077 additions and 726 deletions

View file

@ -26,6 +26,7 @@ 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);
private readonly uniformCache = createCachedFloat32BufferWrite(
@ -57,68 +58,16 @@ export class BrushPipeline {
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
this.pipeline = device.createRenderPipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [commonState.bindGroupLayout, this.bindGroupLayout],
}),
vertex: {
module: smartCompile(device, CommonState.shaderCode, shader),
entryPoint: 'vertex',
buffers: [
{
arrayStride: Float32Array.BYTES_PER_ELEMENT * 6,
attributes: [
{
shaderLocation: 0,
format: 'float32x2',
offset: 0,
},
{
shaderLocation: 1,
format: 'float32x2',
offset: Float32Array.BYTES_PER_ELEMENT * 2,
},
{
shaderLocation: 2,
format: 'float32x2',
offset: Float32Array.BYTES_PER_ELEMENT * 4,
},
],
},
],
},
fragment: {
module: smartCompile(device, CommonState.shaderCode, shader),
entryPoint: 'fragment',
targets: [
{
format: 'rgba16float',
blend: {
color: {
operation: 'max',
srcFactor: 'one',
dstFactor: 'one',
},
alpha: {
operation: 'max',
srcFactor: 'one',
dstFactor: 'one',
},
},
},
],
},
primitive: {
topology: 'triangle-list',
},
});
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({
size: BrushPipeline.UNIFORM_COUNT * Float32Array.BYTES_PER_ELEMENT,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
this.bindGroup = this.bindGroup = this.device.createBindGroup({
this.bindGroup = this.device.createBindGroup({
layout: this.bindGroupLayout,
entries: [
{
@ -315,23 +264,40 @@ export class BrushPipeline {
return offset;
}
public execute(commandEncoder: GPUCommandEncoder, trailMapOut: GPUTextureView) {
public execute(commandEncoder: GPUCommandEncoder, trailMapOut: GPUTextureView): void {
this.executeWithPipeline(commandEncoder, this.pipeline, [trailMapOut]);
}
public executeMultiTarget(
commandEncoder: GPUCommandEncoder,
sourceMapOut: GPUTextureView,
influenceMapOut: GPUTextureView
): void {
this.executeWithPipeline(commandEncoder, this.multiTargetPipeline, [
sourceMapOut,
influenceMapOut,
]);
}
private executeWithPipeline(
commandEncoder: GPUCommandEncoder,
pipeline: GPURenderPipeline,
textureViews: Array<GPUTextureView>
): void {
if (this.lineCount === 0) {
return;
}
const renderPassDescriptor: GPURenderPassDescriptor = {
colorAttachments: [
{
view: trailMapOut,
loadOp: 'load',
storeOp: 'store',
},
],
colorAttachments: textureViews.map<GPURenderPassColorAttachment>((view) => ({
view,
loadOp: 'load',
storeOp: 'store',
})),
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(this.pipeline);
passEncoder.setPipeline(pipeline);
this.commonState.execute(passEncoder);
passEncoder.setBindGroup(1, this.bindGroup);
passEncoder.setVertexBuffer(0, this.vertexBuffer);
@ -344,6 +310,73 @@ export class BrushPipeline {
this.uniforms.destroy();
}
private createPipeline(
shaderModule: GPUShaderModule,
fragmentEntryPoint: string,
colorTargetCount: number
): GPURenderPipeline {
return this.device.createRenderPipeline({
layout: this.device.createPipelineLayout({
bindGroupLayouts: [this.commonState.bindGroupLayout, this.bindGroupLayout],
}),
vertex: {
module: shaderModule,
entryPoint: 'vertex',
buffers: [
{
arrayStride: Float32Array.BYTES_PER_ELEMENT * 6,
attributes: [
{
shaderLocation: 0,
format: 'float32x2',
offset: 0,
},
{
shaderLocation: 1,
format: 'float32x2',
offset: Float32Array.BYTES_PER_ELEMENT * 2,
},
{
shaderLocation: 2,
format: 'float32x2',
offset: Float32Array.BYTES_PER_ELEMENT * 4,
},
],
},
],
},
fragment: {
module: shaderModule,
entryPoint: fragmentEntryPoint,
targets: Array.from(
{ length: colorTargetCount },
() => BrushPipeline.colorTarget
),
},
primitive: {
topology: 'triangle-list',
},
});
}
private static get colorTarget(): GPUColorTargetState {
return {
format: 'rgba16float',
blend: {
color: {
operation: 'max',
srcFactor: 'one',
dstFactor: 'one',
},
alpha: {
operation: 'max',
srcFactor: 'one',
dstFactor: 'one',
},
},
};
}
private static get bindGroupLayout(): GPUBindGroupLayoutDescriptor {
return {
entries: [