9#include <glm/gtc/matrix_access.hpp>
14WGPUStringView vgLabel(
const char *
s) {
17 out.length = WGPU_STRLEN;
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));
40static_assert(
sizeof(VgCullParams) == 288);
52static_assert(
sizeof(VgDrawParams) == 224);
54constexpr const char *kVgCullWgsl = R
"wgsl(
55struct Cluster { u0: vec4u, u1: vec4u, u2: vec4u, u3: vec4u };
58 planes: array<vec4f, 6>,
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>;
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;
103@compute @workgroup_size(64)
104fn cs_main(@builtin(global_invocation_id) gid: vec3u) {
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;
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; }
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;
127constexpr const char *kVgVisWgsl = R
"wgsl(
128struct Cluster { u0: vec4u, u1: vec4u, u2: vec4u, u3: vec4u };
130 mvp: mat4x4f, model: mat4x4f, clip: vec4f, tint: vec4f,
131 lightDir: vec4f, lightColor: vec4f, ambient: vec4f, ids: vec4u,
134 @builtin(position) pos: vec4f,
135 @location(0) normal: vec3f,
136 @location(1) bary: vec3f,
137 @location(2) @interpolate(flat) triBase: u32,
140 @location(0) id: vec2u,
141 @location(1) bary: vec2f,
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>;
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]));
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);
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;
178fn fs_main(in: VSOut) -> FSOut {
180 out.id = vec2u(0x80000000u | params.ids.x, in.triBase);
181 out.bary = in.bary.xy;
186constexpr const char *kVgResolveWgsl = R
"wgsl(
188 mvp: mat4x4f, model: mat4x4f, clip: vec4f, tint: vec4f,
189 lightDir: vec4f, lightColor: vec4f, ambient: vec4f, ids: vec4u,
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;
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]));
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);
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;
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);
237wgpu::Buffer uploadBuffer(wgpu::Device device, wgpu::Queue queue, const char *
name,
238 const void *data, uint64_t
bytes, WGPUBufferUsage
usage) {
239 WGPUBufferDescriptor
desc{};
241 desc.size = (
bytes + 3u) & ~uint64_t(3u);
242 desc.usage =
usage | WGPUBufferUsage_CopyDst;
244 device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor *
>(&
desc));
255 GpuDrivenVgAsset out{};
259 out.positions = uploadBuffer(
device, queue,
"eve_vg_positions", asset.
positions,
261 WGPUBufferUsage_Storage);
262 out.triangles = uploadBuffer(
device, queue,
"eve_vg_triangles", asset.
triangles,
264 WGPUBufferUsage_Storage);
265 out.clusters = uploadBuffer(
device, queue,
"eve_vg_clusters", asset.
clusters,
267 WGPUBufferUsage_Storage);
268 WGPUBufferDescriptor indirect{};
269 indirect.label = vgLabel(
"eve_vg_indirect");
272 WGPUBufferUsage_CopySrc | WGPUBufferUsage_Storage | WGPUBufferUsage_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);
285 const auto it = gpuDrivenVgMeshIds_.find(
mesh);
290 if (!
mesh || vgAssetId >= gpuDrivenVgAssets_.size())
return false;
291 gpuDrivenVgAssets_[vgAssetId].mesh =
mesh;
292 gpuDrivenVgMeshIds_[
mesh] = vgAssetId;
297 uint32_t materialId) {
298 if (vgAssetId >= gpuDrivenVgAssets_.size() || materialId >= gpuDrivenMaterials_.size())
300 auto &asset = gpuDrivenVgAssets_[vgAssetId];
302 asset.materialId = materialId;
304 gpuDrivenVgComputePending_ =
true;
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);
314 gpuDrivenVgCameraPos_ =
eye;
315 gpuDrivenVgFovYDeg_ = fovYDeg;
316 gpuDrivenVgNear_ = nearZ;
317 gpuDrivenVgFar_ = farZ;
322 gpuDrivenVgPlanes_[4] = glm::row(
viewProj, 2);
324 for (glm::vec4 &plane : gpuDrivenVgPlanes_) {
325 const float length = glm::length(glm::vec3(plane));
328 gpuDrivenVgComputePending_ =
true;
329 gpuDrivenVgVisPending_ =
true;
330 gpuDrivenVgResolvePending_ =
true;
331 frameHad3DThisFrame =
true;
335void Graphics::ensureGpuDrivenVgResources() {
336 if (gpuDrivenVgCullPipeline_)
return;
337 WGPUBindGroupLayoutEntry
compute[4]{};
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) {
344 compute[i].visibility = WGPUShaderStage_Compute;
345 compute[i].buffer.type = i == 1 ? WGPUBufferBindingType_ReadOnlyStorage
346 : WGPUBufferBindingType_Storage;
350 compute[3].visibility = WGPUShaderStage_Compute;
351 compute[3].buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
352 WGPUBindGroupLayoutDescriptor cbgl{};
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));
371 WGPUBindGroupLayoutEntry vis[4]{};
372 for (uint32_t i = 0; i < 4; ++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);
380 WGPUBindGroupLayoutDescriptor vbgl{};
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));
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");
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");
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));
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;
434 WGPUBindGroupLayoutDescriptor rbgl{};
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");
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");
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));
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;
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]{};
490 entries[0].buffer = asset.params.Get();
493 entries[1].buffer = asset.clusters.Get();
494 entries[1].size = uint64_t(asset.clusterCount) *
sizeof(GpuVgCluster);
496 entries[2].buffer = asset.indirect.Get();
497 entries[2].size = uint64_t(asset.clusterCount) * 16u;
499 entries[3].buffer = gpuDrivenHzbBuffer_.Get();
500 entries[3].size = gpuDrivenHzbCapacity_;
501 WGPUBindGroupDescriptor bgd{};
502 bgd.layout = gpuDrivenVgComputeSetLayout_.Get();
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);
512 gpuDrivenVgVisibleDiagnostic_ += asset.clusterCount;
514 gpuDrivenVgComputePending_ =
false;
518#if defined(__EMSCRIPTEN__)
519 return gpuDrivenVgVisibleDiagnostic_;
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;
530 device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor *
>(&bd));
531 wgpu::CommandEncoder encoder =
device.CreateCommandEncoder();
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);
538 wgpu::CommandBuffer command = encoder.Finish();
539 queue.Submit(1, &command);
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;
549 WGPUFuture
future = wgpuBufferMapAsync(dst.Get(), WGPUMapMode_Read, 0, totalBytes,
callback);
550 WGPUFutureWaitInfo wait{};
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));
557 for (uint64_t commandOffset = 0; commandOffset < totalBytes /
sizeof(uint32_t);
559 visible += words[commandOffset + 1u];
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};
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;
607 for (uint32_t cluster = 0; cluster < asset.clusterCount; ++cluster) {
609 params.mvp = gpuDrivenVgViewProj_;
610 params.model = asset.model;
611 params.clip = glm::vec4(mesh3dNear, mesh3dFar, 0.f, 0.f);
614 params.ids = glm::uvec4(assetId, cluster, 0
u, 0
u);
615 const uint32_t
offset = arena.alloc(
sizeof(
params), 256);
617 WGPUBindGroupEntry
entries[4]{};
619 entries[0].buffer = arena.buffer.Get();
623 entries[1].buffer = asset.positions.Get();
624 entries[1].size = uint64_t(asset.vertexCount) * 3u *
sizeof(float);
626 entries[2].buffer = asset.triangles.Get();
627 entries[2].size = uint64_t(asset.triangleCount) *
sizeof(uint32_t);
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();
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_;
643 gpuDrivenVgVisPending_ =
false;
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;
658 params.mvp = gpuDrivenVgViewProj_;
659 params.model = asset.model;
666 const uint32_t
offset = arena.alloc(
sizeof(
params), 256);
668 WGPUBindGroupEntry
entries[5]{};
670 entries[0].textureView = slot.visIDView.Get();
672 entries[1].textureView = slot.visBaryView.Get();
674 entries[2].buffer = asset.positions.Get();
675 entries[2].size = uint64_t(asset.vertexCount) * 3u *
sizeof(float);
677 entries[3].buffer = asset.triangles.Get();
678 entries[3].size = uint64_t(asset.triangleCount) *
sizeof(uint32_t);
680 entries[4].buffer = arena.buffer.Get();
683 WGPUBindGroupDescriptor bgd{};
684 bgd.layout = gpuDrivenVgResolveSetLayout_.Get();
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;
693 gpuDrivenVgResolvePending_ =
false;
std::unordered_map< std::string, QuestRuntime > entries
building::EdgeCurveGroup group
wgpu::PopErrorScopeStatus status
std::unique_ptr< gpgpu::GpuBuffer > buffer
std::vector< float > colors
const UnitySourceAsset & source
GPU mesh handle (+ optional CPU morph targets).
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
const GpuVgCluster * clusters
GPU-packed cluster node (std430, 4 x uvec4). Mirrors the virtualgeometry module's VgGpuCluster layout...
Light3DGpu lights[kMaxLights]
std::future< vkb::Instance > future