WIP
This commit is contained in:
parent
34ac200437
commit
39b0160064
136 changed files with 7144 additions and 1965 deletions
29
src/pipelines/diffusion/diffusion-pipeline.test.ts
Normal file
29
src/pipelines/diffusion/diffusion-pipeline.test.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
getSafeInverseDiffusionRate,
|
||||
setDiffusionUniformValues,
|
||||
} from './diffusion-pipeline';
|
||||
|
||||
describe('diffusion pipeline parameters', () => {
|
||||
it('keeps zero diffusion rates finite before writing shader uniforms', () => {
|
||||
const uniformValues = new Float32Array(4);
|
||||
|
||||
setDiffusionUniformValues(uniformValues, {
|
||||
decayRateBrush: 900,
|
||||
decayRateTrails: 970,
|
||||
diffusionRateBrush: 0,
|
||||
diffusionRateTrails: 0,
|
||||
});
|
||||
|
||||
expect(Number.isFinite(uniformValues[0])).toBe(true);
|
||||
expect(Number.isFinite(uniformValues[2])).toBe(true);
|
||||
expect(uniformValues[0]).toBeGreaterThan(0);
|
||||
expect(uniformValues[2]).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('passes valid diffusion rates through as inverse values', () => {
|
||||
expect(getSafeInverseDiffusionRate(2)).toBe(0.5);
|
||||
expect(getSafeInverseDiffusionRate(0.25)).toBe(4);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { appConfig } from '../../config';
|
||||
import {
|
||||
createCachedFloat32BufferWrite,
|
||||
writeFloat32BufferIfChanged,
|
||||
|
|
@ -8,8 +9,36 @@ import { CommonState } from '../common-state/common-state';
|
|||
import shader from './diffuse.wgsl?raw';
|
||||
import { DiffusionSettings } from './diffusion-settings';
|
||||
|
||||
const MIN_DIFFUSION_RATE = appConfig.pipelines.diffusion.minDiffusionRate;
|
||||
|
||||
type DiffusionUniformSettings = Pick<
|
||||
DiffusionSettings,
|
||||
'diffusionRateTrails' | 'decayRateTrails' | 'diffusionRateBrush' | 'decayRateBrush'
|
||||
>;
|
||||
|
||||
export const getSafeInverseDiffusionRate = (diffusionRate: number): number =>
|
||||
1 /
|
||||
(Number.isFinite(diffusionRate) && diffusionRate > MIN_DIFFUSION_RATE
|
||||
? diffusionRate
|
||||
: MIN_DIFFUSION_RATE);
|
||||
|
||||
export const setDiffusionUniformValues = (
|
||||
target: Float32Array,
|
||||
{
|
||||
diffusionRateTrails,
|
||||
decayRateTrails,
|
||||
diffusionRateBrush,
|
||||
decayRateBrush,
|
||||
}: DiffusionUniformSettings
|
||||
): void => {
|
||||
target[0] = getSafeInverseDiffusionRate(diffusionRateTrails);
|
||||
target[1] = decayRateTrails / 1000;
|
||||
target[2] = getSafeInverseDiffusionRate(diffusionRateBrush);
|
||||
target[3] = decayRateBrush / 1000;
|
||||
};
|
||||
|
||||
export class DiffusionPipeline {
|
||||
private static readonly UNIFORM_COUNT = 5;
|
||||
private static readonly UNIFORM_COUNT = 4;
|
||||
|
||||
private readonly bindGroupLayout: GPUBindGroupLayout;
|
||||
private readonly pipeline: GPURenderPipeline;
|
||||
|
|
@ -18,10 +47,10 @@ export class DiffusionPipeline {
|
|||
private readonly uniformCache = createCachedFloat32BufferWrite(
|
||||
DiffusionPipeline.UNIFORM_COUNT
|
||||
);
|
||||
private readonly sampler: GPUSampler;
|
||||
private readonly vertexBuffer: GPUBuffer;
|
||||
|
||||
private bindGroup?: GPUBindGroup;
|
||||
private previousTrailMapIn?: GPUTextureView;
|
||||
private readonly bindGroupsByInput = new WeakMap<GPUTextureView, GPUBindGroup>();
|
||||
|
||||
public constructor(
|
||||
private readonly device: GPUDevice,
|
||||
|
|
@ -57,6 +86,11 @@ export class DiffusionPipeline {
|
|||
size: DiffusionPipeline.UNIFORM_COUNT * Float32Array.BYTES_PER_ELEMENT,
|
||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
||||
});
|
||||
|
||||
this.sampler = this.device.createSampler({
|
||||
magFilter: 'linear',
|
||||
minFilter: 'linear',
|
||||
});
|
||||
}
|
||||
|
||||
public setParameters({
|
||||
|
|
@ -64,13 +98,13 @@ export class DiffusionPipeline {
|
|||
decayRateTrails,
|
||||
diffusionRateBrush,
|
||||
decayRateBrush,
|
||||
anisotropy,
|
||||
}: DiffusionSettings) {
|
||||
this.uniformValues[0] = 1 / diffusionRateTrails;
|
||||
this.uniformValues[1] = decayRateTrails / 1000;
|
||||
this.uniformValues[2] = 1 / diffusionRateBrush;
|
||||
this.uniformValues[3] = decayRateBrush / 1000;
|
||||
this.uniformValues[4] = anisotropy;
|
||||
setDiffusionUniformValues(this.uniformValues, {
|
||||
diffusionRateTrails,
|
||||
decayRateTrails,
|
||||
diffusionRateBrush,
|
||||
decayRateBrush,
|
||||
});
|
||||
writeFloat32BufferIfChanged(
|
||||
this.device,
|
||||
this.uniforms,
|
||||
|
|
@ -84,7 +118,7 @@ export class DiffusionPipeline {
|
|||
trailMapIn: GPUTextureView,
|
||||
trailMapOut: GPUTextureView
|
||||
) {
|
||||
this.ensureBindGroupExists(trailMapIn);
|
||||
const bindGroup = this.getBindGroup(trailMapIn);
|
||||
|
||||
const renderPassDescriptor: GPURenderPassDescriptor = {
|
||||
colorAttachments: [
|
||||
|
|
@ -101,38 +135,39 @@ export class DiffusionPipeline {
|
|||
passEncoder.setPipeline(this.pipeline);
|
||||
passEncoder.setVertexBuffer(0, this.vertexBuffer);
|
||||
this.commonState.execute(passEncoder);
|
||||
passEncoder.setBindGroup(1, this.bindGroup);
|
||||
passEncoder.setBindGroup(1, bindGroup);
|
||||
passEncoder.draw(4, 1);
|
||||
passEncoder.end();
|
||||
}
|
||||
|
||||
private ensureBindGroupExists(trailMapIn: GPUTextureView) {
|
||||
if (this.previousTrailMapIn !== trailMapIn) {
|
||||
this.bindGroup = this.device.createBindGroup({
|
||||
layout: this.bindGroupLayout,
|
||||
entries: [
|
||||
{
|
||||
binding: 0,
|
||||
resource: {
|
||||
buffer: this.uniforms,
|
||||
},
|
||||
},
|
||||
{
|
||||
binding: 1,
|
||||
resource: this.device.createSampler({
|
||||
magFilter: 'linear',
|
||||
minFilter: 'linear',
|
||||
}),
|
||||
},
|
||||
{
|
||||
binding: 2,
|
||||
resource: trailMapIn,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
this.previousTrailMapIn = trailMapIn;
|
||||
private getBindGroup(trailMapIn: GPUTextureView): GPUBindGroup {
|
||||
const cached = this.bindGroupsByInput.get(trailMapIn);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const bindGroup = this.device.createBindGroup({
|
||||
layout: this.bindGroupLayout,
|
||||
entries: [
|
||||
{
|
||||
binding: 0,
|
||||
resource: {
|
||||
buffer: this.uniforms,
|
||||
},
|
||||
},
|
||||
{
|
||||
binding: 1,
|
||||
resource: this.sampler,
|
||||
},
|
||||
{
|
||||
binding: 2,
|
||||
resource: trailMapIn,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
this.bindGroupsByInput.set(trailMapIn, bindGroup);
|
||||
return bindGroup;
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ export interface DiffusionSettings {
|
|||
decayRateTrails: number;
|
||||
diffusionRateBrush: number;
|
||||
decayRateBrush: number;
|
||||
brushEffectDuration: number;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue