载入中...
搜索中...
未找到
GraphicsVirtualGeometry.cpp
浏览该文件的文档.
2
3#include "graphics/Material.h"
4
5#include <algorithm>
6#include <cmath>
7#include <cstring>
8
9#include <glm/gtc/matrix_access.hpp>
10
11namespace eve::graphics::webgpu {
12namespace {
13
14WGPUStringView vgLabel(const char *s) {
15 WGPUStringView out{};
16 out.data = s;
17 out.length = WGPU_STRLEN;
18 return out;
19}
20
21wgpu::ShaderModule vgShader(wgpu::Device device, const char *source) {
22 WGPUShaderSourceWGSL wgsl{};
23 wgsl.chain.sType = WGPUSType_ShaderSourceWGSL;
24 wgsl.code = vgLabel(source);
25 WGPUShaderModuleDescriptor desc{};
26 desc.nextInChain = &wgsl.chain;
27 return device.CreateShaderModule(
28 reinterpret_cast<const wgpu::ShaderModuleDescriptor *>(&desc));
29}
30
31struct VgCullParams {
32 glm::mat4 viewProj{1.f};
33 glm::vec4 planes[6]{};
34 glm::mat4 model{1.f};
35 glm::vec4 cameraPos{};
36 glm::vec4 clipNearFar{};
37 glm::vec4 hzbInfo{};
38 glm::uvec4 counts{};
39};
40static_assert(sizeof(VgCullParams) == 288);
41
42struct VgDrawParams {
43 glm::mat4 mvp{1.f};
44 glm::mat4 model{1.f};
45 glm::vec4 clip{};
46 glm::vec4 tint{1.f};
47 glm::vec4 lightDir{};
48 glm::vec4 lightColor{};
49 glm::vec4 ambient{};
50 glm::uvec4 ids{};
51};
52static_assert(sizeof(VgDrawParams) == 224);
53
54constexpr const char *kVgCullWgsl = R"wgsl(
55struct Cluster { u0: vec4u, u1: vec4u, u2: vec4u, u3: vec4u };
56struct Params {
57 viewProj: mat4x4f,
58 planes: array<vec4f, 6>,
59 model: mat4x4f,
60 cameraPos: vec4f,
61 clipNearFar: vec4f,
62 hzbInfo: vec4f,
63 counts: vec4u,
64};
65struct Command {
66 vertexCount: u32,
67 instanceCount: u32,
68 firstVertex: u32,
69 firstInstance: u32,
70};
71@group(0) @binding(0) var<uniform> params: Params;
72@group(0) @binding(1) var<storage, read> clusters: array<Cluster>;
73@group(0) @binding(2) var<storage, read_write> commands: array<Command>;
74@group(0) @binding(3) var<storage, read> hzb: array<u32>;
75
76fn remainsVisibleAgainstHzb(center: vec3f, radius: f32) -> bool {
77 if (params.clipNearFar.w < 0.5) { return true; }
78 let clip = params.viewProj * vec4f(center, 1.0);
79 if (clip.w <= params.clipNearFar.x) { return true; }
80 let ndc = clip.xyz / clip.w;
81 if (abs(ndc.x) > 1.0 || abs(ndc.y) > 1.0 || ndc.z > 1.0) { return true; }
82 let toEye = normalize(params.cameraPos.xyz - center);
83 let frontClip = params.viewProj * vec4f(center + toEye * radius, 1.0);
84 let frontZ = frontClip.z / max(frontClip.w, 1e-6);
85 let viewDepth = max(length(center - params.cameraPos.xyz), 1e-4);
86 let diameterPx = max(2.0 * radius * params.clipNearFar.z / viewDepth, 1.0);
87 let mip = min(u32(ceil(log2(diameterPx))), u32(params.hzbInfo.x));
88 let width = max(u32(params.hzbInfo.z) >> mip, 1u);
89 let height = max(u32(params.hzbInfo.w) >> mip, 1u);
90 let uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
91 let p0 = clamp(vec2i(uv * vec2f(f32(width), f32(height))) - vec2i(1),
92 vec2i(0), vec2i(i32(width) - 1, i32(height) - 1));
93 let p1 = min(p0 + vec2i(1), vec2i(i32(width) - 1, i32(height) - 1));
94 let base = 16u + hzb[mip];
95 let a = bitcast<f32>(hzb[base + u32(p0.y) * width + u32(p0.x)]);
96 let b = bitcast<f32>(hzb[base + u32(p0.y) * width + u32(p1.x)]);
97 let c = bitcast<f32>(hzb[base + u32(p1.y) * width + u32(p0.x)]);
98 let d = bitcast<f32>(hzb[base + u32(p1.y) * width + u32(p1.x)]);
99 let blocker = min(min(a, b), min(c, d));
100 return blocker >= 0.9999 || frontZ <= blocker + 0.002;
101}
102
103@compute @workgroup_size(64)
104fn cs_main(@builtin(global_invocation_id) gid: vec3u) {
105 let cid = gid.x;
106 if (cid >= params.counts.x) { return; }
107 let cluster = clusters[cid];
108 let localCenter = vec3f(bitcast<f32>(cluster.u0.x), bitcast<f32>(cluster.u0.y),
109 bitcast<f32>(cluster.u0.z));
110 let worldCenter = (params.model * vec4f(localCenter, 1.0)).xyz;
111 let scale = max(length(params.model[0].xyz),
112 max(length(params.model[1].xyz), length(params.model[2].xyz)));
113 let radius = bitcast<f32>(cluster.u0.w) * scale;
114 var visible = true;
115 for (var p = 0u; p < 6u; p = p + 1u) {
116 let plane = params.planes[p];
117 if (dot(plane.xyz, worldCenter) + plane.w < -radius) { visible = false; }
118 }
119 if (visible) { visible = remainsVisibleAgainstHzb(worldCenter, radius); }
120 commands[cid].vertexCount = cluster.u1.y * 3u;
121 commands[cid].instanceCount = select(0u, 1u, visible);
122 commands[cid].firstVertex = 0u;
123 commands[cid].firstInstance = 0u;
124}
125)wgsl";
126
127constexpr const char *kVgVisWgsl = R"wgsl(
128struct Cluster { u0: vec4u, u1: vec4u, u2: vec4u, u3: vec4u };
129struct Params {
130 mvp: mat4x4f, model: mat4x4f, clip: vec4f, tint: vec4f,
131 lightDir: vec4f, lightColor: vec4f, ambient: vec4f, ids: vec4u,
132};
133struct VSOut {
134 @builtin(position) pos: vec4f,
135 @location(0) normal: vec3f,
136 @location(1) bary: vec3f,
137 @location(2) @interpolate(flat) triBase: u32,
138};
139struct FSOut {
140 @location(0) id: vec2u,
141 @location(1) bary: vec2f,
142};
143@group(0) @binding(0) var<uniform> params: Params;
144@group(0) @binding(1) var<storage, read> positions: array<u32>;
145@group(0) @binding(2) var<storage, read> triangles: array<u32>;
146@group(0) @binding(3) var<storage, read> clusters: array<Cluster>;
147
148fn position(index: u32) -> vec3f {
149 let base = index * 3u;
150 return vec3f(bitcast<f32>(positions[base]), bitcast<f32>(positions[base + 1u]),
151 bitcast<f32>(positions[base + 2u]));
152}
153
154@vertex
155fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VSOut {
156 let cluster = clusters[params.ids.y];
157 let triBase = (cluster.u1.x + vertexIndex / 3u) * 3u;
158 let p0 = position(triangles[triBase]);
159 let p1 = position(triangles[triBase + 1u]);
160 let p2 = position(triangles[triBase + 2u]);
161 let w0 = params.model * vec4f(p0, 1.0);
162 let w1 = params.model * vec4f(p1, 1.0);
163 let w2 = params.model * vec4f(p2, 1.0);
164 let corner = vertexIndex % 3u;
165 let world = select(select(w2, w1, corner == 1u), w0, corner == 0u);
166 var out: VSOut;
167 out.pos = params.mvp * world;
168 out.pos.y = -out.pos.y;
169 out.normal = normalize(cross(w1.xyz - w0.xyz, w2.xyz - w0.xyz));
170 out.bary = vec3f(select(0.0, 1.0, corner == 0u),
171 select(0.0, 1.0, corner == 1u),
172 select(0.0, 1.0, corner == 2u));
173 out.triBase = triBase;
174 return out;
175}
176
177@fragment
178fn fs_main(in: VSOut) -> FSOut {
179 var out: FSOut;
180 out.id = vec2u(0x80000000u | params.ids.x, in.triBase);
181 out.bary = in.bary.xy;
182 return out;
183}
184)wgsl";
185
186constexpr const char *kVgResolveWgsl = R"wgsl(
187struct Params {
188 mvp: mat4x4f, model: mat4x4f, clip: vec4f, tint: vec4f,
189 lightDir: vec4f, lightColor: vec4f, ambient: vec4f, ids: vec4u,
190};
191struct Out { @location(0) color: vec4f, @builtin(frag_depth) depth: f32 };
192@group(0) @binding(0) var visID: texture_2d<u32>;
193@group(0) @binding(1) var visBary: texture_2d<f32>;
194@group(0) @binding(2) var<storage, read> positions: array<u32>;
195@group(0) @binding(3) var<storage, read> triangles: array<u32>;
196@group(0) @binding(4) var<uniform> params: Params;
197
198fn position(index: u32) -> vec3f {
199 let base = index * 3u;
200 return vec3f(bitcast<f32>(positions[base]), bitcast<f32>(positions[base + 1u]),
201 bitcast<f32>(positions[base + 2u]));
202}
203
204@vertex
205fn vs_main(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f {
206 let x = f32((index << 1u) & 2u);
207 let y = f32(index & 2u);
208 return vec4f(x * 2.0 - 1.0, 1.0 - y * 2.0, 0.0, 1.0);
209}
210
211@fragment
212fn fs_main(@builtin(position) fragPos: vec4f) -> Out {
213 let coord = vec2i(fragPos.xy);
214 let id = textureLoad(visID, coord, 0);
215 if (id.x != (0x80000000u | params.ids.x)) { discard; }
216 let baryXY = textureLoad(visBary, coord, 0).xy;
217 let bary = vec3f(baryXY, 1.0 - baryXY.x - baryXY.y);
218 let p0 = position(triangles[id.y]);
219 let p1 = position(triangles[id.y + 1u]);
220 let p2 = position(triangles[id.y + 2u]);
221 let w0 = params.model * vec4f(p0, 1.0);
222 let w1 = params.model * vec4f(p1, 1.0);
223 let w2 = params.model * vec4f(p2, 1.0);
224 let world = w0 * bary.x + w1 * bary.y + w2 * bary.z;
225 var normal = normalize(cross(w1.xyz - w0.xyz, w2.xyz - w0.xyz));
226 if (dot(normal, -params.lightDir.xyz) < 0.0) { normal = -normal; }
227 let diffuse = max(dot(normal, normalize(-params.lightDir.xyz)), 0.0);
228 let lit = params.ambient.rgb + params.lightColor.rgb * diffuse;
229 var out: Out;
230 out.color = vec4f(params.tint.rgb * lit, params.tint.a);
231 let clip = params.mvp * world;
232 out.depth = clamp(clip.z / max(clip.w, 1e-6), 0.0, 1.0);
233 return out;
234}
235)wgsl";
236
237wgpu::Buffer uploadBuffer(wgpu::Device device, wgpu::Queue queue, const char *name,
238 const void *data, uint64_t bytes, WGPUBufferUsage usage) {
239 WGPUBufferDescriptor desc{};
240 desc.label = vgLabel(name);
241 desc.size = (bytes + 3u) & ~uint64_t(3u);
242 desc.usage = usage | WGPUBufferUsage_CopyDst;
243 wgpu::Buffer buffer =
244 device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor *>(&desc));
245 queue.WriteBuffer(buffer, 0, data, bytes);
246 return buffer;
247}
248
249} // namespace
250
252 if (!asset.positions || asset.vertexCount <= 0 || !asset.triangles ||
253 asset.triangleCount <= 0 || !asset.clusters || asset.clusterCount <= 0)
255 GpuDrivenVgAsset out{};
256 out.vertexCount = uint32_t(asset.vertexCount);
257 out.triangleCount = uint32_t(asset.triangleCount);
258 out.clusterCount = uint32_t(asset.clusterCount);
259 out.positions = uploadBuffer(device, queue, "eve_vg_positions", asset.positions,
260 uint64_t(asset.vertexCount) * 3u * sizeof(float),
261 WGPUBufferUsage_Storage);
262 out.triangles = uploadBuffer(device, queue, "eve_vg_triangles", asset.triangles,
263 uint64_t(asset.triangleCount) * sizeof(uint32_t),
264 WGPUBufferUsage_Storage);
265 out.clusters = uploadBuffer(device, queue, "eve_vg_clusters", asset.clusters,
266 uint64_t(asset.clusterCount) * sizeof(GpuVgCluster),
267 WGPUBufferUsage_Storage);
268 WGPUBufferDescriptor indirect{};
269 indirect.label = vgLabel("eve_vg_indirect");
270 indirect.size = uint64_t(asset.clusterCount) * 16u;
271 indirect.usage =
272 WGPUBufferUsage_CopySrc | WGPUBufferUsage_Storage | WGPUBufferUsage_Indirect;
273 out.indirect =
274 device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor *>(&indirect));
275 WGPUBufferDescriptor params{};
276 params.label = vgLabel("eve_vg_cull_params");
277 params.size = sizeof(VgCullParams);
278 params.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
279 out.params = device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor *>(&params));
280 gpuDrivenVgAssets_.push_back(std::move(out));
281 return uint32_t(gpuDrivenVgAssets_.size() - 1u);
282}
283
285 const auto it = gpuDrivenVgMeshIds_.find(mesh);
286 return it == gpuDrivenVgMeshIds_.end() ? kInvalidGpuDrivenSlot : it->second;
287}
288
289bool Graphics::gpuDrivenVgAttachToMesh(Mesh *mesh, uint32_t vgAssetId) {
290 if (!mesh || vgAssetId >= gpuDrivenVgAssets_.size()) return false;
291 gpuDrivenVgAssets_[vgAssetId].mesh = mesh;
292 gpuDrivenVgMeshIds_[mesh] = vgAssetId;
293 return true;
294}
295
296bool Graphics::gpuDrivenVgSetInstance(uint32_t vgAssetId, const glm::mat4 &model,
297 uint32_t materialId) {
298 if (vgAssetId >= gpuDrivenVgAssets_.size() || materialId >= gpuDrivenMaterials_.size())
299 return false;
300 auto &asset = gpuDrivenVgAssets_[vgAssetId];
301 asset.model = model;
302 asset.materialId = materialId;
303 asset.active = true;
304 gpuDrivenVgComputePending_ = true;
305 return true;
306}
307
308void Graphics::gpuDrivenVgComputeSection(const glm::mat4 &viewProj, const glm::vec3 &eye,
309 float fovYDeg, float nearZ, float farZ) {
310 if (gbufferSlots.empty() && sceneColorWidth > 0 && sceneColorHeight > 0)
311 createGbufferResources(sceneColorWidth, sceneColorHeight);
312 if (!gbufferSlots.empty()) ensureGpuDrivenResources(1, 1);
313 gpuDrivenVgViewProj_ = viewProj;
314 gpuDrivenVgCameraPos_ = eye;
315 gpuDrivenVgFovYDeg_ = fovYDeg;
316 gpuDrivenVgNear_ = nearZ;
317 gpuDrivenVgFar_ = farZ;
318 gpuDrivenVgPlanes_[0] = glm::row(viewProj, 3) + glm::row(viewProj, 0);
319 gpuDrivenVgPlanes_[1] = glm::row(viewProj, 3) - glm::row(viewProj, 0);
320 gpuDrivenVgPlanes_[2] = glm::row(viewProj, 3) + glm::row(viewProj, 1);
321 gpuDrivenVgPlanes_[3] = glm::row(viewProj, 3) - glm::row(viewProj, 1);
322 gpuDrivenVgPlanes_[4] = glm::row(viewProj, 2);
323 gpuDrivenVgPlanes_[5] = glm::row(viewProj, 3) - glm::row(viewProj, 2);
324 for (glm::vec4 &plane : gpuDrivenVgPlanes_) {
325 const float length = glm::length(glm::vec3(plane));
326 if (length > 1e-6f) plane /= length;
327 }
328 gpuDrivenVgComputePending_ = true;
329 gpuDrivenVgVisPending_ = true;
330 gpuDrivenVgResolvePending_ = true;
331 frameHad3DThisFrame = true;
332 frameHad3D = true;
333}
334
335void Graphics::ensureGpuDrivenVgResources() {
336 if (gpuDrivenVgCullPipeline_) return;
337 WGPUBindGroupLayoutEntry compute[4]{};
338 compute[0].binding = 0;
339 compute[0].visibility = WGPUShaderStage_Compute;
340 compute[0].buffer.type = WGPUBufferBindingType_Uniform;
341 compute[0].buffer.minBindingSize = sizeof(VgCullParams);
342 for (uint32_t i = 1; i < 3; ++i) {
343 compute[i].binding = i;
344 compute[i].visibility = WGPUShaderStage_Compute;
345 compute[i].buffer.type = i == 1 ? WGPUBufferBindingType_ReadOnlyStorage
346 : WGPUBufferBindingType_Storage;
347 compute[i].buffer.minBindingSize = i == 1 ? sizeof(GpuVgCluster) : 16u;
348 }
349 compute[3].binding = 3;
350 compute[3].visibility = WGPUShaderStage_Compute;
351 compute[3].buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
352 WGPUBindGroupLayoutDescriptor cbgl{};
353 cbgl.entryCount = 4;
354 cbgl.entries = compute;
355 gpuDrivenVgComputeSetLayout_ = device.CreateBindGroupLayout(
356 reinterpret_cast<const wgpu::BindGroupLayoutDescriptor *>(&cbgl));
357 WGPUBindGroupLayout rawCompute = gpuDrivenVgComputeSetLayout_.Get();
358 WGPUPipelineLayoutDescriptor cpl{};
359 cpl.bindGroupLayoutCount = 1;
360 cpl.bindGroupLayouts = &rawCompute;
361 gpuDrivenVgComputePipelineLayout_ = device.CreatePipelineLayout(
362 reinterpret_cast<const wgpu::PipelineLayoutDescriptor *>(&cpl));
363 wgpu::ShaderModule cull = vgShader(device, kVgCullWgsl);
364 WGPUComputePipelineDescriptor cpd{};
365 cpd.layout = gpuDrivenVgComputePipelineLayout_.Get();
366 cpd.compute.module = cull.Get();
367 cpd.compute.entryPoint = vgLabel("cs_main");
368 gpuDrivenVgCullPipeline_ = device.CreateComputePipeline(
369 reinterpret_cast<const wgpu::ComputePipelineDescriptor *>(&cpd));
370
371 WGPUBindGroupLayoutEntry vis[4]{};
372 for (uint32_t i = 0; i < 4; ++i) {
373 vis[i].binding = i;
374 vis[i].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
375 vis[i].buffer.type = i == 0 ? WGPUBufferBindingType_Uniform
376 : WGPUBufferBindingType_ReadOnlyStorage;
377 vis[i].buffer.minBindingSize =
378 i == 0 ? sizeof(VgDrawParams) : (i == 3 ? sizeof(GpuVgCluster) : 4u);
379 }
380 WGPUBindGroupLayoutDescriptor vbgl{};
381 vbgl.entryCount = 4;
382 vbgl.entries = vis;
383 gpuDrivenVgVisSetLayout_ = device.CreateBindGroupLayout(
384 reinterpret_cast<const wgpu::BindGroupLayoutDescriptor *>(&vbgl));
385 WGPUBindGroupLayout rawVis = gpuDrivenVgVisSetLayout_.Get();
386 WGPUPipelineLayoutDescriptor vpl{};
387 vpl.bindGroupLayoutCount = 1;
388 vpl.bindGroupLayouts = &rawVis;
389 gpuDrivenVgVisPipelineLayout_ = device.CreatePipelineLayout(
390 reinterpret_cast<const wgpu::PipelineLayoutDescriptor *>(&vpl));
391
392 wgpu::ShaderModule visModule = vgShader(device, kVgVisWgsl);
393 WGPUColorTargetState targets[2]{};
394 targets[0].format = WGPUTextureFormat_RG32Uint;
395 targets[1].format = WGPUTextureFormat_RG16Float;
396 for (auto &target : targets) target.writeMask = WGPUColorWriteMask_All;
397 WGPUFragmentState fs{};
398 fs.module = visModule.Get();
399 fs.entryPoint = vgLabel("fs_main");
400 fs.targetCount = 2;
401 fs.targets = targets;
402 WGPUDepthStencilState depth{};
403 depth.format = WGPUTextureFormat_Depth32Float;
404 depth.depthWriteEnabled = WGPUOptionalBool_True;
405 depth.depthCompare = WGPUCompareFunction_Less;
406 WGPURenderPipelineDescriptor vpd{};
407 vpd.layout = gpuDrivenVgVisPipelineLayout_.Get();
408 vpd.vertex.module = visModule.Get();
409 vpd.vertex.entryPoint = vgLabel("vs_main");
410 vpd.fragment = &fs;
411 vpd.primitive.topology = WGPUPrimitiveTopology_TriangleList;
412 vpd.primitive.cullMode = WGPUCullMode_None;
413 vpd.depthStencil = &depth;
414 vpd.multisample.count = 1;
415 vpd.multisample.mask = 0xffffffffu;
416 gpuDrivenVgVisPipeline_ = device.CreateRenderPipeline(
417 reinterpret_cast<const wgpu::RenderPipelineDescriptor *>(&vpd));
418
419 WGPUBindGroupLayoutEntry resolve[5]{};
420 resolve[0].binding = 0;
421 resolve[0].visibility = WGPUShaderStage_Fragment;
422 resolve[0].texture.sampleType = WGPUTextureSampleType_Uint;
423 resolve[0].texture.viewDimension = WGPUTextureViewDimension_2D;
424 resolve[1] = resolve[0];
425 resolve[1].binding = 1;
426 resolve[1].texture.sampleType = WGPUTextureSampleType_UnfilterableFloat;
427 for (uint32_t i = 2; i < 5; ++i) {
428 resolve[i].binding = i;
429 resolve[i].visibility = WGPUShaderStage_Fragment;
430 resolve[i].buffer.type = i == 4 ? WGPUBufferBindingType_Uniform
431 : WGPUBufferBindingType_ReadOnlyStorage;
432 resolve[i].buffer.minBindingSize = i == 4 ? sizeof(VgDrawParams) : 4u;
433 }
434 WGPUBindGroupLayoutDescriptor rbgl{};
435 rbgl.entryCount = 5;
436 rbgl.entries = resolve;
437 gpuDrivenVgResolveSetLayout_ = device.CreateBindGroupLayout(
438 reinterpret_cast<const wgpu::BindGroupLayoutDescriptor *>(&rbgl));
439 WGPUBindGroupLayout rawResolve = gpuDrivenVgResolveSetLayout_.Get();
440 WGPUPipelineLayoutDescriptor rpl{};
441 rpl.bindGroupLayoutCount = 1;
442 rpl.bindGroupLayouts = &rawResolve;
443 gpuDrivenVgResolvePipelineLayout_ = device.CreatePipelineLayout(
444 reinterpret_cast<const wgpu::PipelineLayoutDescriptor *>(&rpl));
445 wgpu::ShaderModule resolveModule = vgShader(device, kVgResolveWgsl);
446 WGPUColorTargetState color{};
447 color.format = sceneColorFormat;
448 color.writeMask = WGPUColorWriteMask_All;
449 WGPUFragmentState rfs{};
450 rfs.module = resolveModule.Get();
451 rfs.entryPoint = vgLabel("fs_main");
452 rfs.targetCount = 1;
453 rfs.targets = &color;
454 WGPUDepthStencilState resolveDepth = depth;
455 resolveDepth.depthCompare = WGPUCompareFunction_Always;
456 WGPURenderPipelineDescriptor rpd{};
457 rpd.layout = gpuDrivenVgResolvePipelineLayout_.Get();
458 rpd.vertex.module = resolveModule.Get();
459 rpd.vertex.entryPoint = vgLabel("vs_main");
460 rpd.fragment = &rfs;
461 rpd.primitive.topology = WGPUPrimitiveTopology_TriangleList;
462 rpd.depthStencil = &resolveDepth;
463 rpd.multisample.count = 1;
464 rpd.multisample.mask = 0xffffffffu;
465 gpuDrivenVgResolvePipeline_ = device.CreateRenderPipeline(
466 reinterpret_cast<const wgpu::RenderPipelineDescriptor *>(&rpd));
467}
468
469void Graphics::recordGpuDrivenVgCompute(wgpu::CommandEncoder encoder) {
470 if (!gpuDrivenVgComputePending_) return;
471 ensureGpuDrivenVgResources();
472 gpuDrivenVgVisibleDiagnostic_ = 0;
473 for (auto &asset : gpuDrivenVgAssets_) {
474 if (!asset.active) continue;
475 VgCullParams params{};
476 params.viewProj = gpuDrivenVgViewProj_;
477 std::copy(std::begin(gpuDrivenVgPlanes_), std::end(gpuDrivenVgPlanes_), params.planes);
478 params.model = asset.model;
479 params.cameraPos = glm::vec4(gpuDrivenVgCameraPos_, 1.f);
480 const float projScaleY = float(gpuDrivenHzbHeight_) /
481 (2.f * std::tan(glm::radians(gpuDrivenVgFovYDeg_) * 0.5f));
482 params.clipNearFar = glm::vec4(gpuDrivenVgNear_, gpuDrivenVgFar_, projScaleY,
483 gbufferDepthValid_ ? 1.f : 0.f);
484 params.hzbInfo = glm::vec4(float(gpuDrivenHzbOffsets_.size() - 1u), 0.f,
485 float(gpuDrivenHzbWidth_), float(gpuDrivenHzbHeight_));
486 params.counts.x = asset.clusterCount;
487 queue.WriteBuffer(asset.params, 0, &params, sizeof(params));
488 WGPUBindGroupEntry entries[4]{};
489 entries[0].binding = 0;
490 entries[0].buffer = asset.params.Get();
491 entries[0].size = sizeof(params);
492 entries[1].binding = 1;
493 entries[1].buffer = asset.clusters.Get();
494 entries[1].size = uint64_t(asset.clusterCount) * sizeof(GpuVgCluster);
495 entries[2].binding = 2;
496 entries[2].buffer = asset.indirect.Get();
497 entries[2].size = uint64_t(asset.clusterCount) * 16u;
498 entries[3].binding = 3;
499 entries[3].buffer = gpuDrivenHzbBuffer_.Get();
500 entries[3].size = gpuDrivenHzbCapacity_;
501 WGPUBindGroupDescriptor bgd{};
502 bgd.layout = gpuDrivenVgComputeSetLayout_.Get();
503 bgd.entryCount = 4;
504 bgd.entries = entries;
505 wgpu::BindGroup group = device.CreateBindGroup(
506 reinterpret_cast<const wgpu::BindGroupDescriptor *>(&bgd));
507 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
508 pass.SetPipeline(gpuDrivenVgCullPipeline_);
509 pass.SetBindGroup(0, group, 0, nullptr);
510 pass.DispatchWorkgroups((asset.clusterCount + 63u) / 64u, 1, 1);
511 pass.End();
512 gpuDrivenVgVisibleDiagnostic_ += asset.clusterCount;
513 }
514 gpuDrivenVgComputePending_ = false;
515}
516
518#if defined(__EMSCRIPTEN__)
519 return gpuDrivenVgVisibleDiagnostic_;
520#else
521 uint64_t totalBytes = 0;
522 for (const auto &asset : gpuDrivenVgAssets_)
523 totalBytes += uint64_t(asset.clusterCount) * 16u;
524 if (totalBytes == 0) return 0;
525 WGPUBufferDescriptor bd{};
526 bd.label = vgLabel("eve_vg_debug_readback");
527 bd.size = totalBytes;
528 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead;
529 wgpu::Buffer dst =
530 device.CreateBuffer(reinterpret_cast<const wgpu::BufferDescriptor *>(&bd));
531 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
532 uint64_t offset = 0;
533 for (const auto &asset : gpuDrivenVgAssets_) {
534 const uint64_t bytes = uint64_t(asset.clusterCount) * 16u;
535 encoder.CopyBufferToBuffer(asset.indirect, 0, dst, offset, bytes);
536 offset += bytes;
537 }
538 wgpu::CommandBuffer command = encoder.Finish();
539 queue.Submit(1, &command);
540 struct MapState {
541 bool ok = false;
542 } state;
543 WGPUBufferMapCallbackInfo callback{};
544 callback.mode = WGPUCallbackMode_WaitAnyOnly;
545 callback.callback = [](WGPUMapAsyncStatus status, WGPUStringView, void *userdata1, void *) {
546 static_cast<MapState *>(userdata1)->ok = status == WGPUMapAsyncStatus_Success;
547 };
548 callback.userdata1 = &state;
549 WGPUFuture future = wgpuBufferMapAsync(dst.Get(), WGPUMapMode_Read, 0, totalBytes, callback);
550 WGPUFutureWaitInfo wait{};
551 wait.future = future;
552 (void)wgpuInstanceWaitAny(instance.Get(), 1, &wait, UINT64_MAX);
553 if (!state.ok) return 0;
554 const auto *words = static_cast<const uint32_t *>(dst.GetConstMappedRange(0, totalBytes));
555 uint32_t visible = 0;
556 if (words) {
557 for (uint64_t commandOffset = 0; commandOffset < totalBytes / sizeof(uint32_t);
558 commandOffset += 4u)
559 visible += words[commandOffset + 1u];
560 }
561 dst.Unmap();
562 return visible;
563#endif
564}
565
566void Graphics::recordGpuDrivenVgVisibility(wgpu::CommandEncoder encoder) {
567 if (!gpuDrivenVgVisPending_) return;
568 createGbufferResources(sceneColorWidth, sceneColorHeight);
569 ensureGpuDrivenVgResources();
570 const bool loadExisting = gpuDrivenResolvePending_ && !gpuDrivenBuckets_.empty();
571 lastGbufferSlot = currentFrameSlot();
572 GbufferSlot &slot = gbufferSlots[lastGbufferSlot];
573 WGPURenderPassColorAttachment colors[2]{};
574 const WGPUTextureView views[2] = {slot.visIDView.Get(), slot.visBaryView.Get()};
575 for (uint32_t i = 0; i < 2; ++i) {
576 colors[i].view = views[i];
577 colors[i].depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
578 colors[i].loadOp = loadExisting ? WGPULoadOp_Load : WGPULoadOp_Clear;
579 colors[i].storeOp = WGPUStoreOp_Store;
580 colors[i].clearValue = i == 0 ? WGPUColor{4294967295.0, 0.0, 0.0, 0.0}
581 : WGPUColor{0.0, 0.0, 0.0, 0.0};
582 }
583 WGPURenderPassDepthStencilAttachment depth{};
584 depth.view = slot.depthView.Get();
585 depth.depthClearValue = 1.f;
586 depth.depthLoadOp = loadExisting ? WGPULoadOp_Load : WGPULoadOp_Clear;
587 depth.depthStoreOp = WGPUStoreOp_Store;
588 depth.stencilLoadOp = WGPULoadOp_Undefined;
589 depth.stencilStoreOp = WGPUStoreOp_Undefined;
590 WGPURenderPassDescriptor rp{};
591 rp.colorAttachmentCount = 2;
592 rp.colorAttachments = colors;
593 rp.depthStencilAttachment = &depth;
594 wgpu::RenderPassEncoder pass =
595 encoder.BeginRenderPass(reinterpret_cast<const wgpu::RenderPassDescriptor *>(&rp));
596 pass.SetPipeline(gpuDrivenVgVisPipeline_);
597 gpuDrivenVgLastIndirectDrawCount_ = 0;
598 auto &arena = currentUboArena();
599 uint64_t drawCount = 0;
600 for (const auto &asset : gpuDrivenVgAssets_)
601 if (asset.active) drawCount += asset.clusterCount;
602 ensureUboArena(arena, arena.used + drawCount * 256u);
603 for (uint32_t assetId = 0; assetId < gpuDrivenVgAssets_.size(); ++assetId) {
604 auto &asset = gpuDrivenVgAssets_[assetId];
605 if (!asset.active || asset.materialId >= gpuDrivenMaterials_.size()) continue;
606 Material *material = gpuDrivenMaterials_[asset.materialId];
607 for (uint32_t cluster = 0; cluster < asset.clusterCount; ++cluster) {
608 VgDrawParams params{};
609 params.mvp = gpuDrivenVgViewProj_;
610 params.model = asset.model;
611 params.clip = glm::vec4(mesh3dNear, mesh3dFar, 0.f, 0.f);
612 params.tint = glm::vec4(material->getTintR(), material->getTintG(),
613 material->getTintB(), material->getTintA());
614 params.ids = glm::uvec4(assetId, cluster, 0u, 0u);
615 const uint32_t offset = arena.alloc(sizeof(params), 256);
616 queue.WriteBuffer(arena.buffer, offset, &params, sizeof(params));
617 WGPUBindGroupEntry entries[4]{};
618 entries[0].binding = 0;
619 entries[0].buffer = arena.buffer.Get();
620 entries[0].offset = offset;
621 entries[0].size = sizeof(params);
622 entries[1].binding = 1;
623 entries[1].buffer = asset.positions.Get();
624 entries[1].size = uint64_t(asset.vertexCount) * 3u * sizeof(float);
625 entries[2].binding = 2;
626 entries[2].buffer = asset.triangles.Get();
627 entries[2].size = uint64_t(asset.triangleCount) * sizeof(uint32_t);
628 entries[3].binding = 3;
629 entries[3].buffer = asset.clusters.Get();
630 entries[3].size = uint64_t(asset.clusterCount) * sizeof(GpuVgCluster);
631 WGPUBindGroupDescriptor bgd{};
632 bgd.layout = gpuDrivenVgVisSetLayout_.Get();
633 bgd.entryCount = 4;
634 bgd.entries = entries;
635 wgpu::BindGroup group = device.CreateBindGroup(
636 reinterpret_cast<const wgpu::BindGroupDescriptor *>(&bgd));
637 pass.SetBindGroup(0, group, 0, nullptr);
638 pass.DrawIndirect(asset.indirect, uint64_t(cluster) * 16u);
639 ++gpuDrivenVgLastIndirectDrawCount_;
640 }
641 }
642 pass.End();
643 gpuDrivenVgVisPending_ = false;
644}
645
646void Graphics::flushGpuDrivenVgResolve(wgpu::RenderPassEncoder pass) {
647 if (!gpuDrivenVgResolvePending_ || gbufferSlots.empty()) return;
648 ensureGpuDrivenVgResources();
649 GbufferSlot &slot = gbufferSlots[lastGbufferSlot];
650 pass.SetPipeline(gpuDrivenVgResolvePipeline_);
651 auto &arena = currentUboArena();
652 ensureUboArena(arena, arena.used + gpuDrivenVgAssets_.size() * 256u);
653 for (uint32_t assetId = 0; assetId < gpuDrivenVgAssets_.size(); ++assetId) {
654 auto &asset = gpuDrivenVgAssets_[assetId];
655 if (!asset.active || asset.materialId >= gpuDrivenMaterials_.size()) continue;
656 Material *material = gpuDrivenMaterials_[asset.materialId];
657 VgDrawParams params{};
658 params.mvp = gpuDrivenVgViewProj_;
659 params.model = asset.model;
660 params.tint = glm::vec4(material->getTintR(), material->getTintG(),
661 material->getTintB(), material->getTintA());
662 params.lightDir = glm::vec4(glm::vec3(mesh3dLighting.lights[0].posRadius), 0.f);
663 params.lightColor = mesh3dLighting.lights[0].color;
664 params.ambient = mesh3dLighting.ambient;
665 params.ids.x = assetId;
666 const uint32_t offset = arena.alloc(sizeof(params), 256);
667 queue.WriteBuffer(arena.buffer, offset, &params, sizeof(params));
668 WGPUBindGroupEntry entries[5]{};
669 entries[0].binding = 0;
670 entries[0].textureView = slot.visIDView.Get();
671 entries[1].binding = 1;
672 entries[1].textureView = slot.visBaryView.Get();
673 entries[2].binding = 2;
674 entries[2].buffer = asset.positions.Get();
675 entries[2].size = uint64_t(asset.vertexCount) * 3u * sizeof(float);
676 entries[3].binding = 3;
677 entries[3].buffer = asset.triangles.Get();
678 entries[3].size = uint64_t(asset.triangleCount) * sizeof(uint32_t);
679 entries[4].binding = 4;
680 entries[4].buffer = arena.buffer.Get();
681 entries[4].offset = offset;
682 entries[4].size = sizeof(params);
683 WGPUBindGroupDescriptor bgd{};
684 bgd.layout = gpuDrivenVgResolveSetLayout_.Get();
685 bgd.entryCount = 5;
686 bgd.entries = entries;
687 wgpu::BindGroup group = device.CreateBindGroup(
688 reinterpret_cast<const wgpu::BindGroupDescriptor *>(&bgd));
689 pass.SetBindGroup(0, group, 0, nullptr);
690 pass.Draw(3, 1, 0, 0);
691 asset.active = false;
692 }
693 gpuDrivenVgResolvePending_ = false;
694}
695
696} // namespace eve::graphics::webgpu
LogicalId target
bool & active
std::string usage
const std::string & s
std::unordered_map< std::string, QuestRuntime > entries
building::EdgeCurveGroup group
float length
Definition CaveMesh.cpp:94
float planes[6][4]
scene::NodeDesc desc
vkb::Device & device
wgpu::PopErrorScopeStatus status
glm::vec4 clip
glm::uvec4 ids
glm::vec4 tint
glm::mat4 mvp
float u
Definition Grass.cpp:233
size_t offset
std::uint64_t bytes
std::string name
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
OnnxCompute * compute
glm::vec3 eye
Mesh * mesh
glm::mat4 viewProj
glm::mat4 model
Material * material
std::vector< float > colors
bool visible
const UnitySourceAsset & source
std::uint32_t depth
ViewPreparation callback
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
uint32_t debugGpuDrivenVgGpuVisibleCount()
Read back the last VG indirect instance total (native tests).
uint32_t gpuDrivenVgAssetId(Mesh *mesh) const override
Gpu driven vg asset id.
bool gpuDrivenVgSetInstance(uint32_t vgAssetId, const glm::mat4 &model, uint32_t materialId) override
Gpu driven vg set instance.
void gpuDrivenVgComputeSection(const glm::mat4 &viewProj, const glm::vec3 &eye, float fovYDeg, float nearZ, float farZ) override
Gpu driven vg compute section.
uint32_t gpuDrivenVgUpload(const GpuVgAssetUpload &asset) override
Gpu driven vg upload.
bool gpuDrivenVgAttachToMesh(Mesh *mesh, uint32_t vgAssetId) override
Gpu driven vg attach to mesh.
std::vector< ParamSpec > params
constexpr uint32_t kInvalidGpuDrivenSlot
GPU-driven rendering shared constants + std430 GPU layouts.
Neutral GPU upload for one virtual-geometry asset. Raw arrays so the graphics module does not depend ...
const std::uint32_t * triangles
GPU-packed cluster node (std430, 4 x uvec4). Mirrors the virtualgeometry module's VgGpuCluster layout...
Light3DGpu lights[kMaxLights]
Definition Light.h:175
std::future< vkb::Instance > future
Definition Graphics.cpp:182
glm::vec4 lightColor
glm::vec4 ambient
glm::vec4 lightDir
glm::vec4 cameraPos
glm::vec4 hzbInfo
glm::uvec4 counts
glm::vec4 clipNearFar
glm::vec4 color