28constexpr const char* kCullWgsl = R
"wgsl(
29struct U { viewProj: mat4x4f, model: mat4x4f, cameraPos: vec4f, params: vec4f,
30 frustum: array<vec4f, 6>, misc: vec4f };
31struct Visible { counter: atomic<u32>, ids: array<u32> };
32@group(0) @binding(2) var<storage, read_write> cl: array<vec4u>;
33@group(0) @binding(3) var<storage, read_write> vis: Visible;
34@group(0) @binding(5) var<storage, read_write> u: U;
35@group(0) @binding(6) var<storage, read_write> stats: array<atomic<u32>>;
36fn center(id:u32)->vec3f { let v=cl[id*4u]; return vec3f(bitcast<f32>(v.x),bitcast<f32>(v.y),bitcast<f32>(v.z)); }
37fn err(id:u32)->f32 { return bitcast<f32>(cl[id*4u+2u].y); }
38@compute @workgroup_size(64)
39fn main(@builtin(global_invocation_id) gid:vec3u) {
40 let id=gid.x; if(id>=u32(u.misc.x)){return;}
41 let w4=u.model*vec4f(center(id),1); let w=w4.xyz/max(w4.w,1e-6);
42 let sc=max(length(u.model[0].xyz),max(length(u.model[1].xyz),length(u.model[2].xyz)));
43 let r=bitcast<f32>(cl[id*4u].w)*sc;
44 for(var i=0u;i<6u;i=i+1u){let p=u.frustum[i];if(dot(p.xyz,w)+p.w < -r){return;}}
45 let d=max(length(w-u.cameraPos.xyz),1e-4); let ec=err(id)/d;
46 let parent=cl[id*4u+1u].w; var ep=1e30;
47 if(parent!=0xffffffffu){let pw=u.model*vec4f(center(parent),1);ep=err(parent)/max(length(pw.xyz-u.cameraPos.xyz),1e-4);}
48 let leaf=cl[id*4u+2u].z==0u;
49 if(!((ec<=u.params.w||leaf)&&ep>u.params.w)){return;}
50 let slot=atomicAdd(&vis.counter,1u);vis.ids[slot]=id;atomicAdd(&stats[0],1u);
53constexpr const char* kRasterWgsl = R
"wgsl(
54struct U { viewProj: mat4x4f, model: mat4x4f, cameraPos: vec4f, params: vec4f,
55 frustum: array<vec4f, 6>, misc: vec4f };
56struct Visible { counter: atomic<u32>, ids: array<u32> };
57@group(0) @binding(0) var<storage,read_write> pos:array<u32>;
58@group(0) @binding(1) var<storage,read_write> tri:array<u32>;
59@group(0) @binding(2) var<storage,read_write> cl:array<vec4u>;
60@group(0) @binding(3) var<storage,read_write> vis:Visible;
61@group(0) @binding(4) var<storage,read_write> pix:array<atomic<u32>>;
62@group(0) @binding(5) var<storage,read_write> u:U;
63@group(0) @binding(6) var<storage,read_write> stats:array<atomic<u32>>;
64fn p(i:u32)->vec3f{let b=i*3u;return vec3f(bitcast<f32>(pos[b]),bitcast<f32>(pos[b+1u]),bitcast<f32>(pos[b+2u]));}
65@compute @workgroup_size(128)
66fn main(@builtin(global_invocation_id) gid:vec3u){
67 let slot=gid.x/124u;let t=gid.x%124u;if(slot>=atomicLoad(&vis.counter)){return;}
68 let id=vis.ids[slot];let range=cl[id*4u+1u];if(t>=range.y){return;}
69 let b=(range.x+t)*3u;let c0=u.viewProj*(u.model*vec4f(p(tri[b]),1));
70 let c1=u.viewProj*(u.model*vec4f(p(tri[b+1u]),1));let c2=u.viewProj*(u.model*vec4f(p(tri[b+2u]),1));
71 if(c0.w<=1e-6||c1.w<=1e-6||c2.w<=1e-6){return;}
72 let s0=(c0.xy/c0.w*.5+vec2f(.5))*u.params.xy;let s1=(c1.xy/c1.w*.5+vec2f(.5))*u.params.xy;
73 let s2=(c2.xy/c2.w*.5+vec2f(.5))*u.params.xy;
74 let area=(s1.x-s0.x)*(s2.y-s0.y)-(s1.y-s0.y)*(s2.x-s0.x);if(area<=0){return;}
75 let view=vec2i(u.params.xy);let lo=clamp(vec2i(floor(min(min(s0,s1),s2))),vec2i(0),view-vec2i(1));
76 let hi=clamp(vec2i(ceil(max(max(s0,s1),s2))),vec2i(0),view-vec2i(1));let ia=1/area;
77 for(var y=lo.y;y<=hi.y;y=y+1){for(var x=lo.x;x<=hi.x;x=x+1){let q=vec2f(f32(x)+.5,f32(y)+.5);
78 let e0=(s1.x-s0.x)*(q.y-s0.y)-(s1.y-s0.y)*(q.x-s0.x);let e1=(s2.x-s1.x)*(q.y-s1.y)-(s2.y-s1.y)*(q.x-s1.x);
79 let e2=(s0.x-s2.x)*(q.y-s2.y)-(s0.y-s2.y)*(q.x-s2.x);if(e0<0||e1<0||e2<0){continue;}
80 let z=(e1*c0.z/c0.w+e2*c1.z/c1.w+e0*c2.z/c2.w)*ia;let d=u32(clamp(z,0,1)*65535);
81 atomicMin(&pix[u32(y*view.x+x)],(min(d,0xffffu)<<16u)|(id&0xffffu));}}
82 atomicAdd(&stats[4],1u);
86 if (
v->positions)
s->bindBuffer(0,
v->positions);
87 if (
v->triangles)
s->bindBuffer(1,
v->triangles);
88 if (
v->clusters)
s->bindBuffer(2,
v->clusters);
89 if (
v->visible)
s->bindBuffer(3,
v->visible);
90 if (
v->pixels)
s->bindBuffer(4,
v->pixels);
91 if (
v->uniforms)
s->bindBuffer(5,
v->uniforms);
92 if (
v->stats)
s->bindBuffer(6,
v->stats);
95 int n = std::max(1,
w *
h);
96 if (n <= s->pixelCapacity)
return;
103std::vector<uint32_t> packed(
const VirtualGeometryAsset&
a) {
104 std::vector<uint32_t> o;
105 o.reserve(
a.clusters.size() * 16);
106 auto b = [](
float f) {
108 std::memcpy(&
u, &
f, 4);
111 for (
auto&
c :
a.clusters) {
112 o.insert(o.end(), {b(c.cx), b(c.cy), b(c.cz), b(c.r), c.triStart, c.triCount, c.lodLevel, c.parent, b(c.errorR),
113 b(c.errorRScreen), c.childCount, 0});
122 auto*
s =
new webgpu::VgState();
135 auto*
s =
static_cast<webgpu::VgState*
>(
b.state);
149void vgUpload(VgBackend&
b,
const VirtualGeometryAsset&
a) {
150 auto*
s =
static_cast<webgpu::VgState*
>(
b.state);
156 s->positions->uploadBytes(
a.positions.data(),
a.positions.size() * 4);
158 s->triangles->uploadBytes(
a.triangles.data(),
a.triangles.size() * 4);
159 auto p = webgpu::packed(
a);
161 s->clusters->uploadBytes(
p.data(),
p.size() * 4);
165 webgpu::pixels(
s,
s->viewW,
s->viewH);
166 webgpu::bind(
s->cull,
s);
167 webgpu::bind(
s->raster,
s);
170 auto*
s =
static_cast<webgpu::VgState*
>(
b.state);
173 s->uniforms->uploadBytes(&
u,
sizeof(
u));
174 s->cull->bindBuffer(5,
s->uniforms);
175 s->raster->bindBuffer(5,
s->uniforms);
178 auto*
s =
static_cast<webgpu::VgState*
>(
b.state);
179 if (!
s ||
n <= 0)
return;
180 if (
s->visibleCapacity ==
n &&
s->visible &&
s->stats)
return;
181 s->visibleCapacity =
n;
186 s->cull->bindBuffer(3,
s->visible);
187 s->cull->bindBuffer(6,
s->stats);
188 s->raster->bindBuffer(3,
s->visible);
189 s->raster->bindBuffer(6,
s->stats);
192 auto*
s =
static_cast<webgpu::VgState*
>(
b.state);
193 if (!
s || !
s->visible)
return 0;
194 webgpu::pixels(
s,
w,
h);
197 s->visible->fillFloat32(0);
198 s->stats->fillFloat32(0);
199 std::vector<uint32_t> clear(
size_t(
w) *
h, 0xffffffffu);
200 s->pixels->uploadBytes(clear.data(), clear.size() * 4);
204 s->visible->downloadBytes(&
count, 4);
206 return s->lastVisible;
209 auto*
s =
static_cast<webgpu::VgState*
>(
b.state);
210 if (!
s || !
s->pixels)
return false;
211 out.resize(
size_t(
s->viewW) *
s->viewH);
212 s->pixels->downloadBytes(out.data(), out.size() * 4);
Backend-agnostic GPU buffer for compute (storage) or CPU staging transfers. Squirrel-owned; derived c...
Compute program for the WebGPU backend. Accepts WGSL source; GLSL/SPIR-V input is rejected (browsers ...
void webgpuDispatch(ComputeShader *shader, int groupsX, int groupsY, int groupsZ)
Webgpu dispatch.
WebGpuGpuBuffer * webgpuNewBuffer(int byteSize, const std::string &usage)
Webgpu new buffer.
WebGpuComputeShader * webgpuNewShaderFromWgsl(const std::string &wgsl)
Webgpu new shader from wgsl.
void vgReset(VgBackend &be, int visibleCapacity)
Vg reset.
int vgUpdate(VgBackend &be, int clusterCount, int visibleCapacity, int viewW, int viewH)
Vg update.
void vgUploadUniforms(VgBackend &be, const VgUniforms &u)
Vg upload uniforms.
bool vgReadPixels(VgBackend &be, std::vector< uint32_t > &out)
Vg read pixels.
void vgCreate(VgBackend &be)
Vg create.
void vgDestroy(VgBackend &be)
Vg destroy.
void vgUpload(VgBackend &be, const VirtualGeometryAsset &asset)
Vg upload.
gpgpu::GpuBuffer * clusters
gpgpu::GpuBuffer * visible
gpgpu::GpuBuffer * uniforms
gpgpu::GpuBuffer * positions
gpgpu::WebGpuComputeShader * cull
gpgpu::GpuBuffer * triangles
gpgpu::GpuBuffer * pixels
gpgpu::WebGpuComputeShader * raster