近邻探寻
内容纲要
WebGpu已经使用一段时间了,做个近邻探寻的测试。

屏幕被分成9块,红色粒子为观测对象,同一单元格中的粒子显示为黄色,
移动到下一个单元格中的粒子显示为绿色,其他粒子显示为灰色。相邻粒子应该是靠近目标块的粒子。
此外,近邻探寻中使用了双调排序。
。
下面是完整测试例子:
<!DOCTYPE html>
<html>
<head>
<title>近邻探寻测试-indeex</title>
<meta charset="utf-8">
</head>
<body>
<canvas id="webgpu-canvas"></canvas>
<script>
(async () => {
const [adapter, glslang] = await Promise.all([
navigator.gpu.requestAdapter(),
import("https://unpkg.com/@webgpu/glslang@0.0.15/dist/web-devel/glslang.js").then(m => m.default())
]);
const device = await adapter.requestDevice();
const canvas = document.getElementById("webgpu-canvas");
canvas.width = 400;
canvas.height = 400;
const context = canvas.getContext("gpupresent");
const swapChainFormat = "bgra8unorm";
const swapChain = context.configureSwapChain({
device,
format: swapChainFormat
});
const powerN = 4;
const dataNum = 2 ** powerN;
const PARTICLE_NUM = dataNum;
const PARTICLE_SIZE = 3;
const POINT_SIZE = 0.04; // 0.02
const viewRadius = 0.2;
const bucketSize = 2.0 * viewRadius;
const bucketNum = Math.ceil(1.0 / bucketSize);
const bucketIndexNum = bucketNum * bucketNum; // 2d
const particleIndex = 0;
const positionData = new Float32Array(PARTICLE_NUM * PARTICLE_SIZE);
const velocityData = new Float32Array(PARTICLE_NUM * PARTICLE_SIZE);
var buckets = []; // for test
for (let i = 0 ; i < positionData.length / PARTICLE_SIZE ; i++){
positionData[i * PARTICLE_SIZE + 0] = Math.random();
positionData[i * PARTICLE_SIZE + 1] = Math.random();
positionData[i * PARTICLE_SIZE + 2] = 0;
velocityData[i * PARTICLE_SIZE + 0] = (Math.random() * 2.0 - 1.0) * 0.002;
velocityData[i * PARTICLE_SIZE + 1] = (Math.random() * 2.0 - 1.0) * 0.002;
velocityData[i * PARTICLE_SIZE + 2] = 0.0;
// 测试
var bucketX = Math.floor(positionData[i * PARTICLE_SIZE + 0] / bucketSize);
var bucketY = Math.floor(positionData[i * PARTICLE_SIZE + 1] / bucketSize);
var bucketIndex = bucketX + bucketY * bucketNum;
buckets.push([bucketIndex, i]);
}
//console.log("position: ", positionData);
const positionBuffer = device.createBuffer({
size: positionData.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE
});
device.defaultQueue.writeBuffer(positionBuffer, 0, positionData);
const velocityBuffer = device.createBuffer({
size: velocityData.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE
});
device.defaultQueue.writeBuffer(velocityBuffer, 0, velocityData);
//console.log("buckets: ", buckets);
let n = powerN;
for (let p = 0; p < n; p++) {
for(let q = 0; q <=p; q++) {
const d = 1 << (p - q);
for (let i = 0; i < buckets.length; i++) {
const up = ((i >> p) & 2) === 0;
if ((i & d) == 0 && (buckets[i][0] > buckets[i | d][0]) === up) {
const tmp = buckets[i];
buckets[i] = buckets[i | d];
buckets[i | d] = tmp;
}
}
}
}
console.log("bukets sorted: ", buckets);
const bucketsArray = new Uint32Array(PARTICLE_NUM * 2);
const bucketsBuffer = device.createBuffer({
size: bucketsArray.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
});
const workBufferSize = bucketsArray.byteLength;
const workBuffer = device.createBuffer({
size: workBufferSize,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
});
//const resultBufferSize = bucketsArray.byteLength;
//const resultBuffer = device.createBuffer({
// size: resultBufferSize,
// usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
// // 注意 resultBuffer
//});
const bucketRefData = new Uint32Array(bucketIndexNum * 2);
for (let i = 0 ; i < bucketIndexNum ; i++){
bucketRefData[i * 2 + 0] = 65535;
bucketRefData[i * 2 + 1] = 65535;
}
console.log("bucketRef: ", bucketRefData);
const bucketRefBuffer = device.createBuffer({
size: bucketRefData.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
});
device.defaultQueue.writeBuffer(bucketRefBuffer, 0, bucketRefData);
const neighbors = new Uint32Array(PARTICLE_NUM);
//for (let i = 0 ; i < PARTICLE_NUM ; i++){
// neighbors[i] = 65535;
//}
//console.log("neighbors: ", neighbors);
const neighborsBuffer = device.createBuffer({
size: neighbors.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
});
//device.defaultQueue.writeBuffer(neighborsBuffer, 0, neighbors);
//const resultBufferSize = bucketRefData.byteLength; // for bucketRef
//const resultBufferSize = neighbors.byteLength; // for findNeighbors
//const resultBuffer = device.createBuffer({
// size: resultBufferSize,
// usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
// // 注意 resultBuffer: COPY_SRC
//});
var uniformBuffer = device.createBuffer({
size: 2 * 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
});
//const particleUniformBuffer = device.createBuffer({
// size: 4,
// usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
//});
// shader (GLSL)
const computeShader_initialize_buckets = `
#version 450
// constants
#define PARTICLE_NUM ${PARTICLE_NUM}
#define PARTICLE_SIZE ${PARTICLE_SIZE}
#define BUCKET_NUM ${bucketNum}
#define BUCKET_SIZE ${bucketSize}
#define BUCKETIDX_NUM ${bucketIndexNum}
layout(set = 0, binding = 0) buffer Particle {
float particle[ PARTICLE_NUM * PARTICLE_SIZE ];
} particle;
layout(set = 0, binding = 1) buffer Velocity {
float velocity[ PARTICLE_NUM * PARTICLE_SIZE ];
} velocity;
layout(set = 0, binding = 2) buffer BucketsData {
uint buckets[ PARTICLE_NUM * 2 ];
};
layout(set = 0, binding = 3) buffer WorkData {
uint numbers[ PARTICLE_NUM * 2 ];
} workData;
layout(set = 0, binding = 4) buffer BucketRef {
uint bucketRef[ BUCKETIDX_NUM * 2 ];
};
layout(set = 0, binding = 5) buffer Neighbors {
uint neighbors[ PARTICLE_NUM ];
};
//layout(set = 0, binding = 6) buffer Results {
// //uint numbers[ PARTICLE_NUM * 2 ];
// //uint numbers[ BUCKETIDX_NUM * 2 ]; // for bucketRef
// uint numbers[ PARTICLE_NUM ]; // for findNeeighbors
//} resultData;
layout(set = 0, binding = 7) uniform StepUniform {
vec2 pq;
};
//layout(set = 0, binding = 8) uniform ParticleIndex {
// uint particleIndex;
//};
void main()
{
uint index = gl_GlobalInvocationID.x;
if ( index >= PARTICLE_NUM ) { return; }
vec3 position = vec3(
particle.particle[ index * PARTICLE_SIZE + 0 ],
particle.particle[ index * PARTICLE_SIZE + 1 ],
particle.particle[ index * PARTICLE_SIZE + 2 ]
);
uint bucketX = uint( floor(position.x / BUCKET_SIZE) );
uint bucketY = uint( floor(position.y / BUCKET_SIZE) );
uint bucketIndex = bucketX + bucketY * BUCKET_NUM;
buckets[index * 2 + 0 ] = bucketIndex;
buckets[index * 2 + 1 ] = index;
}
`;
const computeShaderSort = `
#version 450
#define N ${powerN}
#define PARTICLE_NUM ${PARTICLE_NUM}
#define PARTICLE_SIZE ${PARTICLE_SIZE}
#define BUCKETIDX_NUM ${bucketIndexNum}
layout(set = 0, binding = 0) buffer Particle {
float particle[ PARTICLE_NUM * PARTICLE_SIZE ];
} particle;
layout(set = 0, binding = 1) buffer Velocity {
float velocity[ PARTICLE_NUM * PARTICLE_SIZE ];
} velocity;
layout(set = 0, binding = 2) buffer BucketsData {
uint buckets[ PARTICLE_NUM * 2 ];
};
layout(set = 0, binding = 3) buffer WorkData {
uint numbers[ PARTICLE_NUM * 2 ];
} workData;
layout(set = 0, binding = 4) buffer BucketRef {
uint bucketRef[ BUCKETIDX_NUM * 2 ];
};
layout(set = 0, binding = 5) buffer Neighbors {
uint neighbors[ PARTICLE_NUM ];
};
//layout(set = 0, binding = 6) buffer ResultData {
// //uint numbers[ PARTICLE_NUM * 2 ];
// //uint numbers[ BUCKETIDX_NUM * 2 ]; // for bucketRef
// uint numbers[ PARTICLE_NUM ]; // for findNeeighbors
//} resultData;
layout(set = 0, binding = 7) uniform StepUniform {
vec2 pq;
};
//layout(set = 0, binding = 8) uniform ParticleIndex {
// uint particleIndex;
//};
const uint n = N;
void main() {
uint index = gl_GlobalInvocationID.x;
uint p = uint(pq.x);
uint q = uint(pq.y);
uint d = 1u << (p - q);
bool up = ((index >> p) & 2u) == 0u;
if ((index & d) == 0 && (buckets[index * 2 + 0] > buckets[(index + d) * 2 + 0]) == up) {
workData.numbers[index * 2 + 0] = buckets[(index + d) * 2 + 0];
workData.numbers[index * 2 + 1] = buckets[(index + d) * 2 + 1];
} else if ((index & d) == d && (buckets[(index - d) * 2 + 0] > buckets[index * 2 + 0]) == up) {
workData.numbers[index * 2 + 0] = buckets[(index - d) * 2 + 0];
workData.numbers[index * 2 + 1] = buckets[(index - d) * 2 + 1];
} else {
workData.numbers[index * 2 + 0] = buckets[index * 2 + 0];
workData.numbers[index * 2 + 1] = buckets[index * 2 + 1];
}
}
`;
const computeShaderWork = `
#version 450
#define PARTICLE_NUM ${PARTICLE_NUM}
#define PARTICLE_SIZE ${PARTICLE_SIZE}
#define BUCKETIDX_NUM ${bucketIndexNum}
layout(set = 0, binding = 0) buffer Particle {
float particle[ PARTICLE_NUM * PARTICLE_SIZE ];
} particle;
layout(set = 0, binding = 1) buffer Velocity {
float velocity[ PARTICLE_NUM * PARTICLE_SIZE ];
} velocity;
layout(set = 0, binding = 2) buffer BucketsData {
uint buckets[ PARTICLE_NUM * 2 ];
};
layout(set = 0, binding = 3) buffer WorkData {
uint numbers[ PARTICLE_NUM * 2 ];
} workData;
layout(set = 0, binding = 4) buffer BucketRef {
uint bucketRef[ BUCKETIDX_NUM * 2 ];
};
layout(set = 0, binding = 5) buffer Neighbors {
uint neighbors[ PARTICLE_NUM ];
};
//layout(set = 0, binding = 6) buffer ResultData {
// //uint numbers[ PARTICLE_NUM * 2 ];
// //uint numbers[ BUCKETIDX_NUM * 2 ]; // for bucketRef
// uint numbers[ PARTICLE_NUM ]; // for findNeeighbors
//} resultData;
layout(set = 0, binding = 7) uniform StepUniform {
vec2 pq;
};
//layout(set = 0, binding = 8) uniform ParticleIndex {
// uint particleIndex;
//};
void main() {
uint index = gl_GlobalInvocationID.x;
buckets[index * 2 + 0] = workData.numbers[index * 2 + 0];
buckets[index * 2 + 1] = workData.numbers[index * 2 + 1];
}
`;
const computeShader_bucketRef = `
#version 450
#define PARTICLE_NUM ${PARTICLE_NUM}
#define PARTICLE_SIZE ${PARTICLE_SIZE}
#define BUCKETIDX_NUM ${bucketIndexNum}
layout(set = 0, binding = 0) buffer Particle {
float particle[ PARTICLE_NUM * PARTICLE_SIZE ];
} particle;
layout(set = 0, binding = 1) buffer Velocity {
float velocity[ PARTICLE_NUM * PARTICLE_SIZE ];
} velocity;
layout(set = 0, binding = 2) buffer BucketsData {
uint buckets[ PARTICLE_NUM * 2 ];
};
layout(set = 0, binding = 3) buffer WorkData {
uint numbers[ PARTICLE_NUM * 2 ];
} workData;
layout(set = 0, binding = 4) buffer BucketRef {
uint bucketRef[ BUCKETIDX_NUM * 2 ];
};
layout(set = 0, binding = 5) buffer Neighbors {
uint neighbors[ PARTICLE_NUM ];
};
//layout(set = 0, binding = 6) buffer ResultData {
// //uint numbers[ PARTICLE_NUM * 2 ];
// //uint numbers[ BUCKETIDX_NUM * 2 ]; // for bucketRef
// uint numbers[ PARTICLE_NUM ]; // for findNeeighbors
//} resultData;
layout(set = 0, binding = 7) uniform StepUniform {
vec2 pq;
};
//layout(set = 0, binding = 8) uniform ParticleIndex {
// uint particleIndex;
//};
void main() {
// dispatch PARTICLE_NUM + 1 ( 0 to PARTICLE_NUM)
uint index = gl_GlobalInvocationID.x;
if (index == 0) {
uint refIndex_start = buckets[index * 2 + 0];
bucketRef[refIndex_start * 2 + 0] = 0;
} else if (index < PARTICLE_NUM) {
uint refIndex_start = buckets[index * 2 + 0];
uint refIndex_end = buckets[(index - 1) * 2 + 0];
if (refIndex_start != refIndex_end) {
bucketRef[refIndex_start * 2 + 0] = index;
bucketRef[refIndex_end * 2 + 1] = index - 1;
}
} else {
uint refIndex_end = buckets[(index - 1) * 2 + 0];
bucketRef[refIndex_end * 2 + 1] = index - 1;
}
}
`;
const computeShader_findNeighbors = `
#version 450
#define PARTICLE_NUM ${PARTICLE_NUM}
#define PARTICLE_SIZE ${PARTICLE_SIZE}
#define BUCKET_NUM ${bucketNum}
#define BUCKET_SIZE ${bucketSize}
#define BUCKETIDX_NUM ${bucketIndexNum}
#define particleIndex ${particleIndex}
layout(set = 0, binding = 0) buffer Particle {
float particle[ PARTICLE_NUM * PARTICLE_SIZE ];
} particle;
layout(set = 0, binding = 1) buffer Velocity {
float velocity[ PARTICLE_NUM * PARTICLE_SIZE ];
} velocity;
layout(set = 0, binding = 2) buffer BucketsData {
uint buckets[ PARTICLE_NUM * 2 ];
};
layout(set = 0, binding = 3) buffer WorkData {
uint numbers[ PARTICLE_NUM * 2 ];
} workData;
layout(set = 0, binding = 4) buffer BucketRef {
uint bucketRef[ BUCKETIDX_NUM * 2 ];
};
layout(set = 0, binding = 5) buffer Neighbors {
uint neighbors[ PARTICLE_NUM ];
};
//layout(set = 0, binding = 6) buffer ResultData {
// //uint numbers[ PARTICLE_NUM * 2 ];
// //uint numbers[ BUCKETIDX_NUM * 2 ]; // for bucketRef
// uint numbers[ PARTICLE_NUM ]; // for findNeeighbors
//} resultData;
layout(set = 0, binding = 7) uniform StepUniform {
vec2 pq;
};
//layout(set = 0, binding = 8) uniform ParticleIndex {
// uint particleIndex;
//};
void findNeighbors(uint index, ivec2 bucketPosition, uint neighborFlag) {
if (bucketPosition.x < 0 || bucketPosition.x >= BUCKET_NUM || bucketPosition.y < 0 || bucketPosition.y >= BUCKET_NUM) {
return;
}
uint bucketIndex = bucketPosition.x + BUCKET_NUM * bucketPosition.y;
uint cellStart = bucketRef[bucketIndex * 2 + 0];
uint cellEnd = bucketRef[bucketIndex * 2 + 1];
if (cellStart == 65535 || cellEnd == 65535) {
return;
}
for (uint i = cellStart; i <= cellEnd; i++) {
if (buckets[i * 2 + 1] == index) {
//neighbors[index] = buckets[i * 2 + 1];
neighbors[index] = neighborFlag;
}
}
}
void main() {
uint index = gl_GlobalInvocationID.x;
uint pIndex = particleIndex;
vec2 position = vec2(
particle.particle[ pIndex * PARTICLE_SIZE + 0 ],
particle.particle[ pIndex * PARTICLE_SIZE + 1 ]
);
vec2 bucketPosition = position / BUCKET_SIZE;
int xOffset = fract(bucketPosition.x) < 0.5 ? -1: 1;
int yOffset = fract(bucketPosition.y) < 0.5 ? -1: 1;
ivec2 bucketPosition00 = ivec2(bucketPosition);
ivec2 bucketPosition10 = bucketPosition00 + ivec2(xOffset, 0);
ivec2 bucketPosition01 = bucketPosition00 + ivec2(0, yOffset);
ivec2 bucketPosition11 = bucketPosition00 + ivec2(xOffset, yOffset);
findNeighbors(index, bucketPosition00, 0);
findNeighbors(index, bucketPosition10, 100);
findNeighbors(index, bucketPosition01, 100);
findNeighbors(index, bucketPosition11, 100);
}
`;
const computeShaderRead = `
#version 450
#define PARTICLE_NUM ${PARTICLE_NUM}
#define PARTICLE_SIZE ${PARTICLE_SIZE}
#define BUCKETIDX_NUM ${bucketIndexNum}
layout(set = 0, binding = 0) buffer Particle {
float particle[ PARTICLE_NUM * PARTICLE_SIZE ];
} particle;
layout(set = 0, binding = 1) buffer Velocity {
float velocity[ PARTICLE_NUM * PARTICLE_SIZE ];
} velocity;
layout(set = 0, binding = 2) buffer BucketsData {
uint buckets[ PARTICLE_NUM * 2 ];
};
layout(set = 0, binding = 3) buffer WorkData {
uint numbers[ PARTICLE_NUM * 2 ];
} workData;
layout(set = 0, binding = 4) buffer BucketRef {
uint bucketRef[ BUCKETIDX_NUM * 2 ];
};
layout(set = 0, binding = 5) buffer Neighbors {
uint neighbors[ PARTICLE_NUM ];
};
//layout(set = 0, binding = 6) buffer ResultData {
// //uint numbers[ PARTICLE_NUM * 2 ];
// //uint numbers[ BUCKETIDX_NUM * 2 ]; // for bucketRef
// uint numbers[ PARTICLE_NUM ]; // for findNeeighbors
//} resultData;
layout(set = 0, binding = 7) uniform StepUniform {
vec2 pq;
};
//layout(set = 0, binding = 8) uniform ParticleIndex {
// uint particleIndex;
//};
void main() {
uint index = gl_GlobalInvocationID.x;
// for buckets
//resultData.numbers[index * 2 + 0 ] = buckets[index * 2 + 0];
//resultData.numbers[index * 2 + 1 ] = buckets[index * 2 + 1];
// for bucketRef
//resultData.numbers[index * 2 + 0 ] = bucketRef[index * 2 + 0];
//resultData.numbers[index * 2 + 1 ] = bucketRef[index * 2 + 1];
// for neighbors
//resultData.numbers[index] = neighbors[index];
}
`;
const computeShader_integrate = `
#version 450
#define PARTICLE_NUM ${PARTICLE_NUM}
#define PARTICLE_SIZE ${PARTICLE_SIZE}
#define BUCKETIDX_NUM ${bucketIndexNum}
layout(set = 0, binding = 0) buffer Particle {
float particle[ PARTICLE_NUM * PARTICLE_SIZE ];
} particle;
layout(set = 0, binding = 1) buffer Velocity {
float velocity[ PARTICLE_NUM * PARTICLE_SIZE ];
} velocity;
layout(set = 0, binding = 2) buffer BucketsData {
uint buckets[ PARTICLE_NUM * 2 ];
};
layout(set = 0, binding = 3) buffer WorkData {
uint numbers[ PARTICLE_NUM * 2 ];
} workData;
layout(set = 0, binding = 4) buffer BucketRef {
uint bucketRef[ BUCKETIDX_NUM * 2 ];
};
layout(set = 0, binding = 5) buffer Neighbors {
uint neighbors[ PARTICLE_NUM ];
};
//layout(set = 0, binding = 6) buffer ResultData {
// //uint numbers[ PARTICLE_NUM * 2 ];
// uint numbers[ PARTICLE_NUM ]; // for findNeeighbors
//} resultData;
layout(set = 0, binding = 7) uniform StepUniform {
vec2 pq;
};
//layout(set = 0, binding = 8) uniform ParticleIndex {
// uint particleIndex;
//};
void main() {
uint index = gl_GlobalInvocationID.x;
if ( index >= PARTICLE_NUM ) { return; }
vec3 position = vec3(
particle.particle[ index * PARTICLE_SIZE + 0 ] + velocity.velocity[ index * PARTICLE_SIZE + 0 ],
particle.particle[ index * PARTICLE_SIZE + 1 ] + velocity.velocity[ index * PARTICLE_SIZE + 1 ],
particle.particle[ index * PARTICLE_SIZE + 2 ] + velocity.velocity[ index * PARTICLE_SIZE + 2 ]
// // particle.particle[ index * PARTICLE_SIZE + 0 ],
// // particle.particle[ index * PARTICLE_SIZE + 1 ],
// // particle.particle[ index * PARTICLE_SIZE + 2 ]
);
//vec3 position =
// particle.particle[ index ] + velocity.velocity[ index ];
if ( position.x <= 0.001 || position.x >= 0.999 ) {
velocity.velocity[ index * PARTICLE_SIZE + 0 ] = - velocity.velocity[ index * PARTICLE_SIZE + 0 ];
//velocity.velocity[ index ].x = - velocity.velocity[ index ].x;
}
//if ( abs( position.y ) >= ROOM_SIZE ) {
if ( position.y <= 0.001 || position.y >= 0.999 ) {
velocity.velocity[ index * PARTICLE_SIZE + 1 ] = - velocity.velocity[ index * PARTICLE_SIZE + 1 ];
//velocity.velocity[ index ].y = - velocity.velocity[ index ].y;
}
//if ( abs( position.z ) >= ROOM_SIZE ) {
if ( position.z <= 0.001 || position.z >= 0.999 ) {
velocity.velocity[ index * PARTICLE_SIZE + 2 ] = - velocity.velocity[ index * PARTICLE_SIZE + 2 ];
//velocity.velocity[ index ].z = - velocity.velocity[ index ].z;
}
particle.particle[ index * PARTICLE_SIZE + 0 ] = position.x;
particle.particle[ index * PARTICLE_SIZE + 1 ] = position.y;
particle.particle[ index * PARTICLE_SIZE + 2 ] = position.z;
//particle.particle[ index ] = position;
}
`;
const bindGroupLayout = device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.COMPUTE,
type: "storage-buffer"
},
{
binding: 1,
visibility: GPUShaderStage.COMPUTE,
type: "storage-buffer"
},
{
binding: 2,
visibility: GPUShaderStage.COMPUTE,
type: "storage-buffer"
},
{
binding: 3,
visibility: GPUShaderStage.COMPUTE,
type: "storage-buffer"
},
{
binding: 4,
visibility: GPUShaderStage.COMPUTE,
type: "storage-buffer"
},
{
binding: 5,
visibility: GPUShaderStage.COMPUTE,
type: "storage-buffer"
},
//{
// binding: 6,
// visibility: GPUShaderStage.COMPUTE,
// type: "storage-buffer"
//},
{
binding: 7,
visibility: GPUShaderStage.COMPUTE,
type: "uniform-buffer"
}
//{
// binding: 8,
// visibility: GPUShaderStage.COMPUTE,
// type: "uniform-buffer"
//}
]
});
const bindGroup = device.createBindGroup({
layout: bindGroupLayout,
entries: [
{
binding: 0,
resource: {
buffer: positionBuffer
}
},
{
binding: 1,
resource: {
buffer: velocityBuffer
}
},
{
binding: 2,
resource: {
buffer: bucketsBuffer
}
},
{
binding: 3,
resource: {
buffer: workBuffer
}
},
{
binding: 4,
resource: {
buffer: bucketRefBuffer
}
},
{
binding: 5,
resource: {
buffer: neighborsBuffer
}
},
//{
// binding: 6,
// resource: {
// buffer: resultBuffer
// }
//},
{
binding: 7,
resource: {
buffer: uniformBuffer
}
}
//{
// binding: 8,
// resource: {
// buffer: particleUniformBuffer
// }
//}
]
});
const computePipeline_InitializeBackets = device.createComputePipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
}),
computeStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(computeShader_initialize_buckets, "compute")
}),
entryPoint: "main"
}
});
const computePipeline_Sort = device.createComputePipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
}),
computeStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(computeShaderSort, "compute")
}),
entryPoint: "main"
}
});
const computePipeline_Work = device.createComputePipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
}),
computeStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(computeShaderWork, "compute")
}),
entryPoint: "main"
}
});
const computePipeline_bucketRef = device.createComputePipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
}),
computeStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(computeShader_bucketRef, "compute")
}),
entryPoint: "main"
}
});
const computePipeline_findNeighbors = device.createComputePipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
}),
computeStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(computeShader_findNeighbors, "compute")
}),
entryPoint: "main"
}
});
const computePipeline_integrate = device.createComputePipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
}),
computeStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(computeShader_integrate, "compute")
}),
entryPoint: "main"
}
});
const computePipeline_Read = device.createComputePipeline({
layout: device.createPipelineLayout({
bindGroupLayouts: [bindGroupLayout]
}),
computeStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(computeShaderRead, "compute")
}),
entryPoint: "main"
}
});
const vs = `
#version 450
layout(location=0) in vec3 vertexPosition;
layout(location=1) in vec3 instancePosition;
//
layout(location=2) in uint neighbor;
layout(location = 0) out vec3 vColor;
const float scale = ${POINT_SIZE};
void main() {
mat4 scaleMTX = mat4(
scale, 0, 0, 0,
0, scale , 0, 0,
0, 0, scale, 0,
instancePosition, 1
);
uint id = gl_InstanceIndex;
if (id == 0) {
vColor = vec3(1.0, 0.0, 0.0);
} else if (neighbor == 0) {
vColor = vec3(1.0, 1.0, 0.0);
} else if (neighbor == 100) {
vColor = vec3(0.0, 1.0, 0.0);
} else {
vColor = vec3(0.7, 0.7, 0.7);
}
gl_Position = scaleMTX * vec4(vertexPosition, 1.0);
}
`;
const fs = `
#version 450
layout(location=0) in vec3 vColor;
layout(location=0) out vec4 fragColor;
void main() {
fragColor = vec4(vColor, 1.0);
}
`;
const cubeData = utils.createCube();
const numVertices = cubeData.positions.length / 3;
const vertexBuffer = device.createBuffer({
size: cubeData.positions.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
device.defaultQueue.writeBuffer(vertexBuffer, 0, cubeData.positions);
const pipeline = device.createRenderPipeline({
layout: device.createPipelineLayout({bindGroupLayouts: []}),
vertexStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(vs, "vertex")
}),
entryPoint: "main"
},
fragmentStage: {
module: device.createShaderModule({
code: glslang.compileGLSL(fs, "fragment")
}),
entryPoint: "main"
},
primitiveTopology: "triangle-list",
vertexState: {
vertexBuffers: [
{
arrayStride: 12,
attributes: [{
shaderLocation: 0,
format: "float3",
offset: 0
}]
},
{
arrayStride: 12,
stepMode: "instance",
attributes: [{
shaderLocation: 1,
format: "float3",
offset: 0
}]
},
{
arrayStride: 4,
stepMode: "instance",
attributes: [{
shaderLocation: 2,
format: "uint",
offset: 0
}]
},
]
},
colorStates: [{
format: swapChainFormat
}]
});
//const gpuReadBuffer = device.createBuffer({
// size: resultBufferSize,
// usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
//});
requestAnimationFrame(function draw() {
// clear neighbors
for (let i = 0 ; i < PARTICLE_NUM ; i++){
neighbors[i] = 65535;
}
device.defaultQueue.writeBuffer(neighborsBuffer, 0, neighbors);
var commandEncoder = device.createCommandEncoder();
var passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(computePipeline_InitializeBackets);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatch(dataNum);
passEncoder.endPass();
device.defaultQueue.submit([commandEncoder.finish()]);
for (let p = 0; p < powerN; p++) { // dataNum -> powerN
for (let q = 0; q <= p; q++) {
Sort(p, q);
}
}
var commandEncoder = device.createCommandEncoder();
var passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(computePipeline_bucketRef);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatch(dataNum + 1);
passEncoder.endPass();
device.defaultQueue.submit([commandEncoder.finish()]);
//const particleIndexData = new Uint32Array([0]);
//device.defaultQueue.writeBuffer(particleUniformBuffer, 0, particleIndexData);
var commandEncoder = device.createCommandEncoder();
var passEncoder = commandEncoder.beginComputePass();
passEncoder.setPipeline(computePipeline_findNeighbors);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatch(dataNum);
passEncoder.endPass();
device.defaultQueue.submit([commandEncoder.finish()]);
var commandEncoder = device.createCommandEncoder();
var passEncoder = commandEncoder.beginComputePass();
//passEncoder.setPipeline(computePipeline_Read);
//passEncoder.setBindGroup(0, bindGroup);
//passEncoder.dispatch(dataNum);
//passEncoder.dispatch(bucketIndexNum); // for bucketRef
passEncoder.setPipeline(computePipeline_integrate);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatch(PARTICLE_NUM);
passEncoder.endPass();
//commandEncoder.copyBufferToBuffer( resultBuffer, 0, gpuReadBuffer, 0, resultBufferSize);
//device.defaultQueue.submit([commandEncoder.finish()]);
//
const textureView = swapChain.getCurrentTexture().createView();
const renderPass = commandEncoder.beginRenderPass({
colorAttachments: [{
attachment: textureView,
loadValue: [0, 0, 0, 1]
}]
});
renderPass.setPipeline(pipeline);
renderPass.setVertexBuffer(0, vertexBuffer);
renderPass.setVertexBuffer(1, positionBuffer);
renderPass.setVertexBuffer(2, neighborsBuffer);
//renderPass.setViewport(0, 0, canvas.width, canvas.height, 0, 1);
renderPass.draw(numVertices, PARTICLE_NUM, 0, 0);
renderPass.endPass();
device.defaultQueue.submit([commandEncoder.finish()]);
//await gpuReadBuffer.mapAsync(GPUMapMode.READ);
//const arrayBuffer = gpuReadBuffer.getMappedRange();
//
//const arrayData = new Uint32Array(arrayBuffer);
//var arrayData2 = [];
//for (let i = 0; i < bucketIndexNum; i++) {
// arrayData2.push([arrayData[i * 2 + 0], arrayData[i * 2 + 1]]);
//}
//console.log("resultData: ", new Uint32Array(arrayBuffer));
//console.log("resultData: ", arrayData);
//console.log("reultData2: ", arrayData2);
requestAnimationFrame(draw);
});
// Sort function
function Sort(p, q) {
var commandEncoder = device.createCommandEncoder();
var passEncoder = commandEncoder.beginComputePass();
var pq = new Float32Array([p, q]);
device.defaultQueue.writeBuffer(uniformBuffer, 0, pq);
passEncoder.setPipeline(computePipeline_Sort);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatch(dataNum);
passEncoder.setPipeline(computePipeline_Work);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.dispatch(dataNum);
passEncoder.endPass();
device.defaultQueue.submit([commandEncoder.finish()]);
}
})();
</script>
</body>
</html>
code enjoy! 🐾🐾🐾🐾🐾🐾🐾🐾🐌🐝
作者:indeex
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。