16#include <glm/gtc/matrix_access.hpp>
21WGPUStringView
label(
const char*
s) {
24 out.length = WGPU_STRLEN;
28wgpu::ShaderModule shaderModule(wgpu::Device
device,
const char*
source) {
29 WGPUShaderSourceWGSL wgsl{};
30 wgsl.chain.sType = WGPUSType_ShaderSourceWGSL;
32 WGPUShaderModuleDescriptor
desc{};
33 desc.nextInChain = &wgsl.chain;
34 return device.CreateShaderModule(
reinterpret_cast<const wgpu::ShaderModuleDescriptor*
>(&
desc));
37wgpu::ShaderModule shaderModule(wgpu::Device
device,
const std::string&
source) {
38 WGPUShaderSourceWGSL wgsl{};
39 wgsl.chain.sType = WGPUSType_ShaderSourceWGSL;
40 wgsl.code.data =
source.data();
41 wgsl.code.length =
source.size();
42 WGPUShaderModuleDescriptor
desc{};
43 desc.nextInChain = &wgsl.chain;
44 return device.CreateShaderModule(
reinterpret_cast<const wgpu::ShaderModuleDescriptor*
>(&
desc));
57static_assert(
sizeof(CullInput) == 144);
60static_assert(
sizeof(VisibleInstance) == 208);
71static_assert(
sizeof(CullParams) == 240);
73struct VisIndirectCommand {
79static_assert(
sizeof(VisIndirectCommand) == 16);
81struct HzbBuildParams {
85static_assert(
sizeof(HzbBuildParams) == 32);
88constexpr uint32_t kHzbHeaderWords = 16;
90constexpr const char* kHzbBuildWgsl = R
"wgsl(
91struct BuildParams { info: vec4u, source: vec4u };
92@group(0) @binding(0) var<uniform> params: BuildParams;
93@group(0) @binding(1) var sourceDepth: texture_depth_2d;
94@group(0) @binding(2) var<storage, read_write> hzb: array<u32>;
96fn readHzb(base: u32, width: u32, height: u32, p: vec2i) -> f32 {
97 let q = clamp(p, vec2i(0), vec2i(i32(width) - 1, i32(height) - 1));
98 return bitcast<f32>(hzb[base + u32(q.y) * width + u32(q.x)]);
101@compute @workgroup_size(8, 8)
102fn cs_main(@builtin(global_invocation_id) gid: vec3u) {
103 let mip = params.info.x;
104 let width = params.info.y;
105 let height = params.info.z;
106 if (gid.x >= width || gid.y >= height) { return; }
109 depth = textureLoad(sourceDepth, vec2i(gid.xy), 0);
111 let previousWidth = params.source.x;
112 let previousHeight = params.source.y;
113 let previousBase = 16u + params.info.w;
114 let p = vec2i(gid.xy * 2u);
115 depth = max(readHzb(previousBase, previousWidth, previousHeight, p),
116 max(readHzb(previousBase, previousWidth, previousHeight, p + vec2i(1, 0)),
117 max(readHzb(previousBase, previousWidth, previousHeight, p + vec2i(0, 1)),
118 readHzb(previousBase, previousWidth, previousHeight, p + vec2i(1, 1)))));
120 let outputBase = 16u + hzb[mip];
121 hzb[outputBase + gid.y * width + gid.x] = bitcast<u32>(depth);
125constexpr const char* kCullWgsl = R
"wgsl(
131 terrainWaveTint: vec4f,
137struct VisibleInstance {
143 reflectionProbeSlots: vec4u,
144 reflectionProbeCenter: array<vec4f, 2>,
145 reflectionProbeExtent: array<vec4f, 2>,
148 terrainWaveTint: vec4f,
152 planes: array<vec4f, 6>,
159struct IndirectCommand {
161 instanceCount: atomic<u32>,
166struct VisIndirectCommand {
168 instanceCount: atomic<u32>,
172@group(0) @binding(0) var<uniform> params: CullParams;
173@group(0) @binding(1) var<storage, read> inputs: array<CullInput>;
174@group(0) @binding(2) var<storage, read_write> visibleInstances: array<VisibleInstance>;
175@group(0) @binding(3) var<storage, read_write> commands: array<IndirectCommand>;
176@group(0) @binding(4) var<storage, read_write> visCommands: array<VisIndirectCommand>;
177@group(0) @binding(5) var<storage, read> hzb: array<u32>;
179fn remainsVisibleAgainstDepth(center: vec3f, radius: f32) -> bool {
180 if (params.clipNearFar.w < 0.5) { return true; }
181 let clip = params.viewProj * vec4f(center, 1.0);
182 if (clip.w <= params.clipNearFar.x) { return true; }
183 let ndc = clip.xyz / clip.w;
184 if (abs(ndc.x) > 1.0 || abs(ndc.y) > 1.0 || ndc.z > 1.0) { return true; }
186 let toEye = normalize(params.cameraPos.xyz - center);
187 let frontClip = params.viewProj * vec4f(center + toEye * radius, 1.0);
188 let frontZ = frontClip.z / max(frontClip.w, 1e-6);
189 let viewDepth = max(length(center - params.cameraPos.xyz), 1e-4);
190 let radiusPx = radius * params.clipNearFar.z / viewDepth;
191 let diameterPx = max(radiusPx * 2.0, 1.0);
192 let mip = min(u32(ceil(log2(diameterPx))), u32(params.hzbInfo.x));
193 let width = max(u32(params.hzbInfo.z) >> mip, 1u);
194 let height = max(u32(params.hzbInfo.w) >> mip, 1u);
195 let uv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
196 let p0 = clamp(vec2i(uv * vec2f(f32(width), f32(height))) - vec2i(1),
197 vec2i(0), vec2i(i32(width) - 1, i32(height) - 1));
198 let p1 = min(p0 + vec2i(1), vec2i(i32(width) - 1, i32(height) - 1));
199 let base = 16u + hzb[mip];
200 let a = bitcast<f32>(hzb[base + u32(p0.y) * width + u32(p0.x)]);
201 let b = bitcast<f32>(hzb[base + u32(p0.y) * width + u32(p1.x)]);
202 let c = bitcast<f32>(hzb[base + u32(p1.y) * width + u32(p0.x)]);
203 let d = bitcast<f32>(hzb[base + u32(p1.y) * width + u32(p1.x)]);
204 let blocker = min(min(a, b), min(c, d));
205 // All conservative samples must contain geometry closer than the sphere.
206 // The epsilon prevents the object's own previous-frame depth from culling it.
207 return blocker >= 0.9999 || frontZ <= blocker + 0.002;
210@compute @workgroup_size(64)
211fn cs_main(@builtin(global_invocation_id) gid: vec3u) {
213 if (index >= params.counts.x) { return; }
214 let input = inputs[index];
215 let center = (input.model * vec4f(input.bounds.xyz, 1.0)).xyz;
216 let scale = max(length(input.model[0].xyz),
217 max(length(input.model[1].xyz), length(input.model[2].xyz)));
218 let radius = input.bounds.w * scale;
219 for (var p = 0u; p < 6u; p = p + 1u) {
220 let plane = params.planes[p];
221 if (dot(plane.xyz, center) + plane.w < -radius) { return; }
223 if (!remainsVisibleAgainstDepth(center, radius)) { return; }
224 let local = atomicAdd(&commands[input.bucket].instanceCount, 1u);
225 atomicAdd(&visCommands[input.bucket].instanceCount, 1u);
226 var visible: VisibleInstance;
227 visible.model = input.model;
229 visible.materialId = 0u;
231 visible.lodGroupId = 0u;
232 visible.reflectionProbeSlots = vec4u(0u);
233 visible.reflectionProbeCenter[0] = vec4f(0.0);
234 visible.reflectionProbeCenter[1] = vec4f(0.0);
235 visible.reflectionProbeExtent[0] = vec4f(0.0);
236 visible.reflectionProbeExtent[1] = vec4f(0.0);
237 visible.color = input.color;
238 visible.terrainWave = input.terrainWave;
239 visible.terrainWaveTint = input.terrainWaveTint;
240 visibleInstances[input.outputBase + local] = visible;
244constexpr const char* kGpuDrivenVertWgsl = R
"wgsl(
245struct Light3D { posRadius: vec4f, color: vec4f };
254 lights: array<Light3D, 8>,
264 @location(0) pos: vec3f,
265 @location(1) normal: vec3f,
266 @location(2) uv: vec2f,
269 @builtin(position) pos: vec4f,
270 @location(0) vNormal: vec3f,
271 @location(1) vUV: vec2f,
272 @location(2) vTint: vec4f,
273 @location(3) vWorldPos: vec3f,
274 @location(4) vCameraPos: vec3f,
275 @location(5) vViewPos: vec3f,
277@group(0) @binding(0) var<uniform> ubo: Frame;
278struct VisibleInstance {
284 reflectionProbeSlots: vec4u,
285 reflectionProbeCenter: array<vec4f, 2>,
286 reflectionProbeExtent: array<vec4f, 2>,
289 terrainWaveTint: vec4f,
291@group(1) @binding(0) var<storage, read> visibleInstances: array<VisibleInstance>;
293fn inverse3x3(m: mat3x3f) -> mat3x3f {
294 let a = m[0].x; let b = m[1].x; let c = m[2].x;
295 let d = m[0].y; let e = m[1].y; let f = m[2].y;
296 let g = m[0].z; let h = m[1].z; let i = m[2].z;
297 let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
299 vec3f((e * i - f * h) / det, (f * g - d * i) / det, (d * h - e * g) / det),
300 vec3f((c * h - b * i) / det, (a * i - c * g) / det, (b * g - a * h) / det),
301 vec3f((b * f - c * e) / det, (c * d - a * f) / det, (a * e - b * d) / det));
304fn terrainFastSinCos(value: vec4f) -> vec4f {
305 var x = fract(value * 0.15915494309189535);
308 let sine = x * (7.61 - 35.2 * x2) / (1.0 + x2 * (11.2 + 3.6 * x2));
312fn terrainDetailWave(worldPosition: ptr<function, vec3f>, heightMask: f32,
313 terrainWave: vec4f, terrainWaveTint: vec4f) -> vec3f {
314 if (terrainWave.w < 0.5) { return vec3f(1.0); }
315 let waveXSize = vec4f(0.012, 0.02, 0.06, 0.024) * terrainWave.y;
316 let waveZSize = vec4f(0.006, 0.02, 0.02, 0.05) * terrainWave.y;
317 var waves = (*worldPosition).x * waveXSize + (*worldPosition).z * waveZSize;
318 waves += terrainWave.x * vec4f(0.3, 0.5, 0.4, 1.2) * 4.0;
319 waves = terrainFastSinCos(waves);
320 let lighting = dot(waves, vec4f(0.6742, 0.6742, 0.2697, 0.1349)) * 0.7;
321 let displacement = vec2f(dot(waves, vec4f(0.024, 0.04, -0.12, 0.096)),
322 dot(waves, vec4f(0.006, 0.02, -0.02, 0.1))) * heightMask;
323 let offset = displacement * terrainWave.z;
324 *worldPosition = vec3f((*worldPosition).x - offset.x, (*worldPosition).y,
325 (*worldPosition).z - offset.y);
326 return 2.0 * mix(vec3f(0.5), terrainWaveTint.rgb, vec3f(lighting));
330fn vs_main(in: VSIn, @builtin(instance_index) instanceIndex: u32) -> VSOut {
331 let sourceModel = visibleInstances[instanceIndex].model;
332 let instanceColor = visibleInstances[instanceIndex].color;
333 let terrainWave = visibleInstances[instanceIndex].terrainWave;
334 let terrainWaveTint = visibleInstances[instanceIndex].terrainWaveTint;
335 var model = sourceModel;
336 if (ubo.surface.w > 0.5) {
337 let origin = sourceModel[3].xyz;
338 var forward = vec3f(ubo.cameraPos.x - origin.x, 0.0, ubo.cameraPos.z - origin.z);
339 forward = select(vec3f(0, 0, 1), normalize(forward), length(forward) > 1e-6);
340 let right = vec3f(forward.z, 0, -forward.x);
341 model = mat4x4f(vec4f(right * length(sourceModel[0].xyz), 0),
342 vec4f(0, length(sourceModel[1].xyz), 0, 0),
343 vec4f(forward * length(sourceModel[2].xyz), 0), sourceModel[3]);
345 var worldPosition = (model * vec4f(in.pos, 1.0)).xyz;
346 let waveTint = terrainDetailWave(&worldPosition, in.uv.y, terrainWave, terrainWaveTint);
347 let world = vec4f(worldPosition, 1.0);
349 out.pos = ubo.mvp * world;
350 out.pos.y = -out.pos.y;
351 out.vWorldPos = world.xyz;
352 out.vViewPos = (ubo.view * world).xyz;
353 let normalMatrix = transpose(inverse3x3(mat3x3f(model[0].xyz, model[1].xyz,
355 out.vNormal = normalize(normalMatrix * in.normal);
357 out.vTint = ubo.tint * instanceColor * vec4f(waveTint, 1.0);
358 out.vCameraPos = ubo.cameraPos.xyz;
373 auto found = gpuDrivenMeshIds_.find(
mesh);
374 if (
found != gpuDrivenMeshIds_.end())
return found->second;
375 const uint32_t
id =
static_cast<uint32_t
>(gpuDrivenMeshes_.size());
376 gpuDrivenMeshes_.push_back(
mesh);
377 gpuDrivenMeshIds_.emplace(
mesh,
id);
384 if (
found != gpuDrivenMaterialIds_.end())
return found->second;
386 if (!gpuDrivenMaterialFree_.empty()) {
387 id = gpuDrivenMaterialFree_.back();
388 gpuDrivenMaterialFree_.pop_back();
391 id =
static_cast<uint32_t
>(gpuDrivenMaterials_.size());
392 gpuDrivenMaterials_.push_back(
material);
394 gpuDrivenMaterialIds_.emplace(
material,
id);
409 "cannot release a null GPU-driven material"));
412 const uint32_t slot =
found->second;
413 gpuDrivenMaterialIds_.erase(
found);
414 gpuDrivenMaterials_[slot] =
nullptr;
415 gpuDrivenMaterialFree_.push_back(slot);
420 if (!gpuDrivenEnabled_ || !
device || gpuDrivenComputePending_ || gpuDrivenDrawPending_ || !
instances ||
423 using BucketKey = std::pair<uint32_t, uint32_t>;
424 std::map<BucketKey, std::vector<const GpuInstance*>> grouped;
427 if (instance.
meshId >= gpuDrivenMeshes_.size() || instance.
materialId >= gpuDrivenMaterials_.size())
436 grouped.begin(), grouped.end(), 0
u, [](uint32_t
count,
const auto& entry) {
437 return count + ((static_cast<uint32_t>(entry.second.size()) + 15u) & ~uint32_t(15u));
439 ensureGpuDrivenResources(
paddedCount,
static_cast<uint32_t
>(grouped.size()));
440 if (!gpuDrivenRenderPipeline_ || !gpuDrivenVisibleBuffer_ || !gpuDrivenIndirectBuffer_)
return false;
443 std::vector<GpuIndirectCommand>
commands;
445 gpuDrivenBuckets_.clear();
447 for (
const auto& [
key, bucketInstances] : grouped) {
450 auto* gpu =
static_cast<GpuMesh*
>(
mesh->gpuHandle);
452 if (!bound)
throw Exception(
"%s", bound.error()->message().c_str());
455 command.instanceCount =
static_cast<uint32_t
>(bucketInstances.size());
457 gpuDrivenBuckets_.push_back(
459 for (uint32_t i = 0; i < bucketInstances.size(); ++i) {
463 outputBase += (
static_cast<uint32_t
>(bucketInstances.size()) + 15u) & ~uint32_t(15u);
465 queue.WriteBuffer(gpuDrivenVisibleBuffer_, 0,
visible.data(),
visible.size() *
sizeof(VisibleInstance));
467 gpuDrivenDrawPending_ =
true;
468 frameHad3DThisFrame =
true;
474 if (!gpuDrivenEnabled_ || !
device || gpuDrivenComputePending_ || gpuDrivenDrawPending_)
482 if (required < batch.buffer.offsetBytes || required > batch.
buffer.
sizeBytes)
486 std::vector<GpuDrivenBucket> buckets(batch.
bucketCount);
487 uint64_t coveredInstances = 0;
492 bucket.meshId >= gpuDrivenMeshes_.size() ||
bucket.materialId >= gpuDrivenMaterials_.size())
498 auto* gpu =
static_cast<GpuMesh*
>(
mesh->gpuHandle);
501 coveredInstances =
end;
506 if (!gpuDrivenResidentRenderPipeline_ || !gpuDrivenIndirectBuffer_)
509 WGPUBuffer rawBuffer{};
513 gpuDrivenBuckets_ = std::move(buckets);
514 gpuDrivenResidentBuffer_ = rawBuffer;
517 gpuDrivenResidentDrawPending_ =
true;
518 gpuDrivenDrawPending_ =
true;
519 frameHad3DThisFrame =
true;
524void Graphics::ensureGpuDrivenResources(uint32_t
instanceCount, uint32_t bucketCount) {
525 if (!gpuDrivenCullPipeline_) {
526 WGPUBindGroupLayoutEntry computeEntries[6]{};
527 computeEntries[0].binding = 0;
528 computeEntries[0].visibility = WGPUShaderStage_Compute;
529 computeEntries[0].buffer.type = WGPUBufferBindingType_Uniform;
530 computeEntries[0].buffer.minBindingSize =
sizeof(CullParams);
531 for (uint32_t i = 1; i < 5; ++i) {
532 computeEntries[i].binding = i;
533 computeEntries[i].visibility = WGPUShaderStage_Compute;
534 computeEntries[i].buffer.type =
535 i == 1 ? WGPUBufferBindingType_ReadOnlyStorage : WGPUBufferBindingType_Storage;
537 computeEntries[1].buffer.minBindingSize =
sizeof(CullInput);
538 computeEntries[2].buffer.minBindingSize =
sizeof(VisibleInstance);
539 computeEntries[3].buffer.minBindingSize =
sizeof(uint32_t) * 5;
540 computeEntries[4].buffer.minBindingSize =
sizeof(VisIndirectCommand);
541 WGPUBindGroupLayoutDescriptor cbgl{};
542 cbgl.label =
label(
"eve_gpu_driven_compute_bgl");
543 computeEntries[5].binding = 5;
544 computeEntries[5].visibility = WGPUShaderStage_Compute;
545 computeEntries[5].buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
547 cbgl.entries = computeEntries;
548 gpuDrivenComputeSetLayout_ =
549 device.CreateBindGroupLayout(
reinterpret_cast<const wgpu::BindGroupLayoutDescriptor*
>(&cbgl));
551 WGPUBindGroupLayoutEntry renderEntry{};
552 renderEntry.binding = 0;
553 renderEntry.visibility = WGPUShaderStage_Vertex;
554 renderEntry.buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
555 renderEntry.buffer.minBindingSize =
sizeof(VisibleInstance);
556 WGPUBindGroupLayoutDescriptor rbgl{};
557 rbgl.label =
label(
"eve_gpu_driven_render_bgl");
559 rbgl.entries = &renderEntry;
560 gpuDrivenRenderSetLayout_ =
561 device.CreateBindGroupLayout(
reinterpret_cast<const wgpu::BindGroupLayoutDescriptor*
>(&rbgl));
563 WGPUBindGroupLayout computeLayout = gpuDrivenComputeSetLayout_.Get();
564 WGPUPipelineLayoutDescriptor cpl{};
565 cpl.label =
label(
"eve_gpu_driven_compute_layout");
566 cpl.bindGroupLayoutCount = 1;
567 cpl.bindGroupLayouts = &computeLayout;
568 gpuDrivenComputePipelineLayout_ =
569 device.CreatePipelineLayout(
reinterpret_cast<const wgpu::PipelineLayoutDescriptor*
>(&cpl));
571 WGPUBindGroupLayout renderLayouts[2] = {mesh3dSetLayout.Get(), gpuDrivenRenderSetLayout_.Get()};
572 WGPUPipelineLayoutDescriptor rpl{};
573 rpl.label =
label(
"eve_gpu_driven_render_layout");
574 rpl.bindGroupLayoutCount = 2;
575 rpl.bindGroupLayouts = renderLayouts;
576 gpuDrivenRenderPipelineLayout_ =
577 device.CreatePipelineLayout(
reinterpret_cast<const wgpu::PipelineLayoutDescriptor*
>(&rpl));
579 wgpu::ShaderModule cullModule = shaderModule(device, kCullWgsl);
580 WGPUComputePipelineDescriptor cpd{};
581 cpd.label =
label(
"eve_gpu_driven_cull");
582 cpd.layout = gpuDrivenComputePipelineLayout_.Get();
583 cpd.compute.module = cullModule.Get();
584 cpd.compute.entryPoint =
label(
"cs_main");
585 gpuDrivenCullPipeline_ =
586 device.CreateComputePipeline(
reinterpret_cast<const wgpu::ComputePipelineDescriptor*
>(&cpd));
588 WGPUBindGroupLayoutEntry hzbEntries[3]{};
589 hzbEntries[0].binding = 0;
590 hzbEntries[0].visibility = WGPUShaderStage_Compute;
591 hzbEntries[0].buffer.type = WGPUBufferBindingType_Uniform;
592 hzbEntries[0].buffer.hasDynamicOffset =
true;
593 hzbEntries[0].buffer.minBindingSize =
sizeof(HzbBuildParams);
594 hzbEntries[1].binding = 1;
595 hzbEntries[1].visibility = WGPUShaderStage_Compute;
596 hzbEntries[1].texture.sampleType = WGPUTextureSampleType_Depth;
597 hzbEntries[1].texture.viewDimension = WGPUTextureViewDimension_2D;
598 hzbEntries[2].binding = 2;
599 hzbEntries[2].visibility = WGPUShaderStage_Compute;
600 hzbEntries[2].buffer.type = WGPUBufferBindingType_Storage;
601 WGPUBindGroupLayoutDescriptor hzbBgl{};
602 hzbBgl.label =
label(
"eve_gpu_driven_hzb_bgl");
603 hzbBgl.entryCount = 3;
604 hzbBgl.entries = hzbEntries;
605 gpuDrivenHzbSetLayout_ =
606 device.CreateBindGroupLayout(
reinterpret_cast<const wgpu::BindGroupLayoutDescriptor*
>(&hzbBgl));
607 WGPUBindGroupLayout hzbLayout = gpuDrivenHzbSetLayout_.Get();
608 WGPUPipelineLayoutDescriptor hzbPl{};
609 hzbPl.bindGroupLayoutCount = 1;
610 hzbPl.bindGroupLayouts = &hzbLayout;
611 gpuDrivenHzbPipelineLayout_ =
612 device.CreatePipelineLayout(
reinterpret_cast<const wgpu::PipelineLayoutDescriptor*
>(&hzbPl));
613 wgpu::ShaderModule hzbModule = shaderModule(device, kHzbBuildWgsl);
614 WGPUComputePipelineDescriptor hzbPd{};
615 hzbPd.label =
label(
"eve_gpu_driven_hzb_build");
616 hzbPd.layout = gpuDrivenHzbPipelineLayout_.Get();
617 hzbPd.compute.module = hzbModule.Get();
618 hzbPd.compute.entryPoint =
label(
"cs_main");
619 gpuDrivenHzbPipeline_ =
620 device.CreateComputePipeline(
reinterpret_cast<const wgpu::ComputePipelineDescriptor*
>(&hzbPd));
622 WGPUVertexAttribute
attrs[3]{};
623 attrs[0].format = WGPUVertexFormat_Float32x3;
625 attrs[0].shaderLocation = 0;
626 attrs[1].format = WGPUVertexFormat_Float32x3;
627 attrs[1].offset = 12;
628 attrs[1].shaderLocation = 1;
629 attrs[2].format = WGPUVertexFormat_Float32x2;
630 attrs[2].offset = 24;
631 attrs[2].shaderLocation = 2;
632 WGPUVertexBufferLayout vb{};
634 vb.stepMode = WGPUVertexStepMode_Vertex;
635 vb.attributeCount = 3;
636 vb.attributes =
attrs;
637 WGPUDepthStencilState
depth{};
638 depth.format = WGPUTextureFormat_Depth32Float;
639 depth.depthWriteEnabled = WGPUOptionalBool_True;
640 depth.depthCompare = WGPUCompareFunction_Less;
641 WGPUColorTargetState
target{};
642 target.format = sceneColorFormat;
643 target.writeMask = WGPUColorWriteMask_All;
644 wgpu::ShaderModule vertModule = shaderModule(device, kGpuDrivenVertWgsl);
645 std::string residentVertSource = kGpuDrivenVertWgsl;
646 const std::string modelsDecl =
647 "struct VisibleInstance {\n"
650 " materialId: u32,\n"
652 " lodGroupId: u32,\n"
653 " reflectionProbeSlots: vec4u,\n"
654 " reflectionProbeCenter: array<vec4f, 2>,\n"
655 " reflectionProbeExtent: array<vec4f, 2>,\n"
657 " terrainWave: vec4f,\n"
658 " terrainWaveTint: vec4f,\n"
660 "@group(1) @binding(0) var<storage, read> visibleInstances: array<VisibleInstance>;";
661 const std::string residentDecl =
662 "struct ResidentInstance { model: mat4x4f, meshId: u32, materialId: u32, "
663 "flags: u32, lodGroupId: u32, reflectionProbeSlots: vec4u, "
664 "reflectionProbeCenter: array<vec4f, 2>, reflectionProbeExtent: array<vec4f, 2>, color: vec4f, "
665 "terrainWave: vec4f, terrainWaveTint: vec4f };\n"
666 "@group(1) @binding(0) var<storage, read> residentInstances: "
667 "array<ResidentInstance>;";
668 residentVertSource.replace(residentVertSource.find(modelsDecl), modelsDecl.size(), residentDecl);
669 const std::string modelRead =
670 "let sourceModel = visibleInstances[instanceIndex].model;\n"
671 " let instanceColor = visibleInstances[instanceIndex].color;\n"
672 " let terrainWave = visibleInstances[instanceIndex].terrainWave;\n"
673 " let terrainWaveTint = visibleInstances[instanceIndex].terrainWaveTint;";
674 residentVertSource.replace(residentVertSource.find(modelRead), modelRead.size(),
675 "let sourceModel = residentInstances[instanceIndex].model;\n"
676 " let instanceColor = residentInstances[instanceIndex].color;\n"
677 " let terrainWave = residentInstances[instanceIndex].terrainWave;\n"
678 " let terrainWaveTint = residentInstances[instanceIndex].terrainWaveTint;");
679 wgpu::ShaderModule residentVertModule = shaderModule(device, residentVertSource);
681 WGPUFragmentState fs{};
682 fs.module = fragModule.Get();
683 fs.entryPoint =
label(
"fs_main");
686 WGPURenderPipelineDescriptor rpd{};
687 rpd.label =
label(
"eve_gpu_driven_render");
688 rpd.layout = gpuDrivenRenderPipelineLayout_.Get();
689 rpd.vertex.module = vertModule.Get();
690 rpd.vertex.entryPoint =
label(
"vs_main");
691 rpd.vertex.bufferCount = 1;
692 rpd.vertex.buffers = &vb;
694 rpd.primitive.topology = WGPUPrimitiveTopology_TriangleList;
695 rpd.primitive.frontFace = WGPUFrontFace_CW;
696 rpd.primitive.cullMode = WGPUCullMode_None;
697 rpd.depthStencil = &
depth;
698 rpd.multisample.count = sceneColorSamples;
699 rpd.multisample.mask = 0xFFFFFFFFu;
700 gpuDrivenRenderPipeline_ =
701 device.CreateRenderPipeline(
reinterpret_cast<const wgpu::RenderPipelineDescriptor*
>(&rpd));
702 rpd.label =
label(
"eve_gpu_driven_resident_render");
703 rpd.vertex.module = residentVertModule.Get();
704 gpuDrivenResidentRenderPipeline_ =
705 device.CreateRenderPipeline(
reinterpret_cast<const wgpu::RenderPipelineDescriptor*
>(&rpd));
706 rpd.label =
label(
"eve_gpu_driven_canvas");
707 rpd.vertex.module = vertModule.Get();
708 target.format = WGPUTextureFormat_RGBA8Unorm;
709 rpd.multisample.count = 1;
710 gpuDrivenCanvasPipeline_ =
711 device.CreateRenderPipeline(
reinterpret_cast<const wgpu::RenderPipelineDescriptor*
>(&rpd));
712 rpd.label =
label(
"eve_gpu_driven_resident_canvas");
713 rpd.vertex.module = residentVertModule.Get();
714 gpuDrivenResidentCanvasPipeline_ =
715 device.CreateRenderPipeline(
reinterpret_cast<const wgpu::RenderPipelineDescriptor*
>(&rpd));
716 rpd.label =
label(
"eve_gpu_driven_hdr_canvas");
717 rpd.vertex.module = vertModule.Get();
718 target.format = WGPUTextureFormat_RGBA16Float;
719 gpuDrivenHdrCanvasPipeline_ =
720 device.CreateRenderPipeline(
reinterpret_cast<const wgpu::RenderPipelineDescriptor*
>(&rpd));
721 rpd.label =
label(
"eve_gpu_driven_resident_hdr_canvas");
722 rpd.vertex.module = residentVertModule.Get();
723 gpuDrivenResidentHdrCanvasPipeline_ =
724 device.CreateRenderPipeline(
reinterpret_cast<const wgpu::RenderPipelineDescriptor*
>(&rpd));
726 WGPUBufferDescriptor pbd{};
727 pbd.label =
label(
"eve_gpu_driven_params");
728 pbd.size =
sizeof(CullParams);
729 pbd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
730 gpuDrivenParamsBuffer_ =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&pbd));
731 pbd.label =
label(
"eve_gpu_driven_hzb_params");
733 gpuDrivenHzbParamsBuffer_ =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&pbd));
736 bool recreateGroups =
false;
737 const uint32_t hzbWidth = std::max(gbufferWidth, 1);
738 const uint32_t hzbHeight = std::max(gbufferHeight, 1);
739 if (!gpuDrivenHzbBuffer_ || gpuDrivenHzbWidth_ != hzbWidth || gpuDrivenHzbHeight_ != hzbHeight) {
740 gpuDrivenHzbWidth_ = hzbWidth;
741 gpuDrivenHzbHeight_ = hzbHeight;
742 gpuDrivenHzbOffsets_.clear();
743 uint32_t words = kHzbHeaderWords;
744 for (uint32_t
w = hzbWidth,
h = hzbHeight; gpuDrivenHzbOffsets_.size() < kMaxHzbMips;
745 w = std::max(
w >> 1u, 1u),
h = std::max(
h >> 1u, 1u)) {
746 gpuDrivenHzbOffsets_.push_back(words - kHzbHeaderWords);
748 if (
w == 1 &&
h == 1)
break;
750 WGPUBufferDescriptor hzbBd{};
751 hzbBd.label =
label(
"eve_gpu_driven_hzb");
752 hzbBd.size = uint64_t(words) *
sizeof(uint32_t);
753 gpuDrivenHzbCapacity_ = hzbBd.size;
754 hzbBd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage;
755 gpuDrivenHzbBuffer_ =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&hzbBd));
756 queue.WriteBuffer(gpuDrivenHzbBuffer_, 0, gpuDrivenHzbOffsets_.data(),
757 gpuDrivenHzbOffsets_.size() *
sizeof(uint32_t));
758 recreateGroups =
true;
761 const uint64_t inputBytes = uint64_t(
instanceCount) *
sizeof(CullInput);
762 if (!gpuDrivenInputBuffer_ || gpuDrivenInputCapacity_ < inputBytes) {
763 gpuDrivenInputCapacity_ = grownCapacity(gpuDrivenInputCapacity_, inputBytes);
764 WGPUBufferDescriptor bd{};
765 bd.label =
label(
"eve_gpu_driven_inputs");
766 bd.size = gpuDrivenInputCapacity_;
767 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage;
768 gpuDrivenInputBuffer_ =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
769 recreateGroups =
true;
771 const uint64_t visibleBytes = uint64_t(
instanceCount) *
sizeof(VisibleInstance);
772 if (!gpuDrivenVisibleBuffer_ || gpuDrivenVisibleCapacity_ < visibleBytes) {
773 gpuDrivenVisibleCapacity_ = grownCapacity(gpuDrivenVisibleCapacity_, visibleBytes);
774 WGPUBufferDescriptor bd{};
775 bd.label =
label(
"eve_gpu_driven_visible");
776 bd.size = gpuDrivenVisibleCapacity_;
777 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage;
778 gpuDrivenVisibleBuffer_ =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
779 recreateGroups =
true;
782 if (!gpuDrivenIndirectBuffer_ || gpuDrivenIndirectCapacity_ < indirectBytes) {
783 gpuDrivenIndirectCapacity_ = grownCapacity(gpuDrivenIndirectCapacity_, indirectBytes);
784 WGPUBufferDescriptor bd{};
785 bd.label =
label(
"eve_gpu_driven_indirect");
786 bd.size = gpuDrivenIndirectCapacity_;
788 WGPUBufferUsage_CopyDst | WGPUBufferUsage_CopySrc | WGPUBufferUsage_Storage | WGPUBufferUsage_Indirect;
789 gpuDrivenIndirectBuffer_ =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
790 recreateGroups =
true;
792 const uint64_t visIndirectBytes = uint64_t(bucketCount) *
sizeof(VisIndirectCommand);
793 if (!gpuDrivenVisIndirectBuffer_ || gpuDrivenVisIndirectCapacity_ < visIndirectBytes) {
794 gpuDrivenVisIndirectCapacity_ = grownCapacity(gpuDrivenVisIndirectCapacity_, visIndirectBytes);
795 WGPUBufferDescriptor bd{};
796 bd.label =
label(
"eve_gpu_driven_vis_indirect");
797 bd.size = gpuDrivenVisIndirectCapacity_;
798 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Storage | WGPUBufferUsage_Indirect;
799 gpuDrivenVisIndirectBuffer_ =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
800 recreateGroups =
true;
804 recreateGroups =
true;
805 if (recreateGroups || !gpuDrivenComputeBindGroup_) {
806 WGPUBindGroupEntry
entries[6]{};
808 entries[0].buffer = gpuDrivenParamsBuffer_.Get();
809 entries[0].size =
sizeof(CullParams);
811 entries[1].buffer = gpuDrivenInputBuffer_.Get();
812 entries[1].size = gpuDrivenInputCapacity_;
814 entries[2].buffer = gpuDrivenVisibleBuffer_.Get();
815 entries[2].size = gpuDrivenVisibleCapacity_;
817 entries[3].buffer = gpuDrivenIndirectBuffer_.Get();
818 entries[3].size = gpuDrivenIndirectCapacity_;
820 entries[4].buffer = gpuDrivenVisIndirectBuffer_.Get();
821 entries[4].size = gpuDrivenVisIndirectCapacity_;
823 const uint32_t depthSlot = gbufferDepthValid_ ? lastGbufferSlot : currentFrameSlot();
824 entries[5].buffer = gpuDrivenHzbBuffer_.Get();
825 entries[5].size = gpuDrivenHzbCapacity_;
826 WGPUBindGroupDescriptor bgd{};
827 bgd.label =
label(
"eve_gpu_driven_compute_bg");
828 bgd.layout = gpuDrivenComputeSetLayout_.Get();
831 gpuDrivenComputeBindGroup_ =
device.CreateBindGroup(
reinterpret_cast<const wgpu::BindGroupDescriptor*
>(&bgd));
833 WGPUBindGroupEntry renderEntry{};
834 renderEntry.binding = 0;
835 renderEntry.buffer = gpuDrivenVisibleBuffer_.Get();
836 renderEntry.size = gpuDrivenVisibleCapacity_;
837 WGPUBindGroupDescriptor rbgd{};
838 rbgd.label =
label(
"eve_gpu_driven_render_bg");
839 rbgd.layout = gpuDrivenRenderSetLayout_.Get();
841 rbgd.entries = &renderEntry;
842 gpuDrivenRenderBindGroup_ =
device.CreateBindGroup(
reinterpret_cast<const wgpu::BindGroupDescriptor*
>(&rbgd));
844 WGPUBindGroupEntry hzbEntries[3]{};
845 hzbEntries[0].binding = 0;
846 hzbEntries[0].buffer = gpuDrivenHzbParamsBuffer_.Get();
847 hzbEntries[0].size =
sizeof(HzbBuildParams);
848 hzbEntries[1].binding = 1;
849 hzbEntries[1].textureView = gbufferSlots[depthSlot].depthView.Get();
850 hzbEntries[2].binding = 2;
851 hzbEntries[2].buffer = gpuDrivenHzbBuffer_.Get();
852 hzbEntries[2].size = gpuDrivenHzbCapacity_;
853 WGPUBindGroupDescriptor hzbBg{};
854 hzbBg.label =
label(
"eve_gpu_driven_hzb_bg");
855 hzbBg.layout = gpuDrivenHzbSetLayout_.Get();
856 hzbBg.entryCount = 3;
857 hzbBg.entries = hzbEntries;
858 gpuDrivenHzbBindGroup_ =
device.CreateBindGroup(
reinterpret_cast<const wgpu::BindGroupDescriptor*
>(&hzbBg));
865 if (
instances[i].meshId >= gpuDrivenMeshes_.size())
return false;
866 auto* gpu =
static_cast<GpuMesh*
>(gpuDrivenMeshes_[
instances[i].meshId]->gpuHandle);
867 if (!gpu || !gpu->indexBuffer)
return false;
870 gpuDrivenVisible_.clear();
871 gpuDrivenBuckets_.clear();
872 gpuDrivenComputePending_ =
false;
873 gpuDrivenDrawPending_ =
false;
879 if (sceneColorWidth > 0 && sceneColorHeight > 0) createSceneColorResources(sceneColorWidth, sceneColorHeight);
880 if (gbufferSlots.empty() && sceneColorWidth > 0 && sceneColorHeight > 0)
881 createGbufferResources(sceneColorWidth, sceneColorHeight);
890 for (glm::vec4& plane :
params.planes) {
891 const float length = glm::length(glm::vec3(plane));
896 glm::vec4(
float(gbufferWidth),
float(gbufferHeight), gbufferWidth > 0 ? 1.f /
float(gbufferWidth) : 0.f,
897 gbufferHeight > 0 ? 1.f / float(gbufferHeight) : 0.f);
898 const float projScaleY = float(gbufferHeight) / (2.f * std::tan(glm::radians(fovYDeg) * 0.5f));
899 params.clipNearFar = glm::vec4(nearZ, farZ, projScaleY, gbufferDepthValid_ ? 1.f : 0.f);
901 glm::vec4(
float(gpuDrivenHzbOffsets_.size() - 1u), 0.f,
float(gpuDrivenHzbWidth_),
float(gpuDrivenHzbHeight_));
902 uint32_t mipWidth = gpuDrivenHzbWidth_;
903 uint32_t mipHeight = gpuDrivenHzbHeight_;
904 uint32_t previousWidth = mipWidth;
905 uint32_t previousHeight = mipHeight;
906 for (uint32_t mip = 0; mip < gpuDrivenHzbOffsets_.size(); ++mip) {
907 HzbBuildParams build{};
908 build.info = glm::uvec4(mip, mipWidth, mipHeight, mip == 0 ? 0
u : gpuDrivenHzbOffsets_[mip - 1]);
909 build.source = glm::uvec4(previousWidth, previousHeight, 0
u, 0
u);
910 queue.WriteBuffer(gpuDrivenHzbParamsBuffer_, uint64_t(mip) * 256u, &build,
sizeof(build));
911 previousWidth = mipWidth;
912 previousHeight = mipHeight;
913 mipWidth = std::max(mipWidth >> 1u, 1u);
914 mipHeight = std::max(mipHeight >> 1u, 1u);
917 using BucketKey = std::pair<uint32_t, uint32_t>;
918 std::map<BucketKey, std::vector<const GpuInstance*>> grouped;
919 for (
const GpuInstance& instance : gpuDrivenPending_)
920 grouped[{instance.meshId, instance.materialId}].push_back(&instance);
922 std::vector<CullInput>
inputs;
923 std::vector<GpuIndirectCommand>
commands;
924 std::vector<VisIndirectCommand> visCommands;
925 inputs.reserve(gpuDrivenPending_.size());
927 visCommands.reserve(grouped.size());
929 for (
const auto& [
key, bucketInstances] : grouped) {
932 auto* gpu =
static_cast<GpuMesh*
>(
mesh->gpuHandle);
933 const uint32_t bucketIndex =
static_cast<uint32_t
>(gpuDrivenBuckets_.size());
941 command.firstInstance = 0;
943 VisIndirectCommand visCommand{};
944 visCommand.vertexCount = gpu->indexCount;
945 visCommand.firstInstance = 0;
946 visCommands.push_back(visCommand);
947 for (
const GpuInstance* instance : bucketInstances) {
949 input.model = instance->model;
950 input.color = instance->color;
951 input.terrainWave = instance->terrainWave;
952 input.terrainWaveTint = instance->terrainWaveTint;
954 ? glm::vec4(
mesh->boundsCx,
mesh->boundsCy,
mesh->boundsCz,
mesh->boundsRadius)
955 : glm::vec4(0.f, 0.f, 0.f, 1e20f);
956 input.bucket = bucketIndex;
960 const glm::vec3
center = glm::vec3(
input.model * glm::vec4(glm::vec3(
input.bounds), 1.f));
962 std::max({glm::length(glm::vec3(
input.model[0])), glm::length(glm::vec3(
input.model[1])),
963 glm::length(glm::vec3(
input.model[2]))});
965 for (
const glm::vec4& plane :
params.planes) {
966 if (glm::dot(glm::vec3(plane),
center) + plane.w < -
input.bounds.w *
scale) {
971 if (
visible) gpuDrivenVisible_.push_back(*instance);
976 outputBase += (
static_cast<uint32_t
>(bucketInstances.size()) + 15u) & ~uint32_t(15u);
981 queue.WriteBuffer(gpuDrivenParamsBuffer_, 0, &
params,
sizeof(
params));
982 queue.WriteBuffer(gpuDrivenInputBuffer_, 0,
inputs.data(),
inputs.size() *
sizeof(CullInput));
984 queue.WriteBuffer(gpuDrivenVisIndirectBuffer_, 0, visCommands.data(),
985 visCommands.size() *
sizeof(VisIndirectCommand));
986 gpuDrivenDispatchCount_ =
static_cast<uint32_t
>(
inputs.size());
987 gpuDrivenLastBucketCount_ =
static_cast<uint32_t
>(
commands.size());
992#if defined(__EMSCRIPTEN__)
993 return static_cast<uint32_t
>(gpuDrivenVisible_.size());
995 if (!gpuDrivenIndirectBuffer_ || gpuDrivenLastBucketCount_ == 0)
return 0;
997 WGPUBufferDescriptor bd{};
998 bd.label =
label(
"eve_gpu_driven_debug_readback");
1000 bd.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead;
1001 wgpu::Buffer dst =
device.CreateBuffer(
reinterpret_cast<const wgpu::BufferDescriptor*
>(&bd));
1002 wgpu::CommandEncoder encoder =
device.CreateCommandEncoder();
1003 encoder.CopyBufferToBuffer(gpuDrivenIndirectBuffer_, 0, dst, 0,
size);
1004 wgpu::CommandBuffer command = encoder.Finish();
1005 queue.Submit(1, &command);
1010 WGPUBufferMapCallbackInfo
callback{};
1011 callback.mode = WGPUCallbackMode_WaitAnyOnly;
1012 callback.callback = [](WGPUMapAsyncStatus
status, WGPUStringView,
void* userdata1,
void*) {
1013 static_cast<MapState*
>(userdata1)->
ok =
status == WGPUMapAsyncStatus_Success;
1016 WGPUFuture
future = wgpuBufferMapAsync(dst.Get(), WGPUMapMode_Read, 0,
size,
callback);
1017 WGPUFutureWaitInfo wait{};
1019 (void)wgpuInstanceWaitAny(instance.Get(), 1, &wait, UINT64_MAX);
1020 if (!
state.ok)
return 0;
1032 if (!gpuDrivenComputePending_ || gpuDrivenBuckets_.empty())
return;
1033 frameHad3DThisFrame =
true;
1035 gpuDrivenDrawPending_ =
true;
1036 gpuDrivenPending_.clear();
1039void Graphics::recordGpuDrivenCompute(wgpu::CommandEncoder encoder) {
1040 if ((gpuDrivenComputePending_ || gpuDrivenVgComputePending_) && gbufferDepthValid_ && gpuDrivenHzbPipeline_ &&
1041 gpuDrivenHzbBindGroup_) {
1042 uint32_t
width = gpuDrivenHzbWidth_;
1043 uint32_t
height = gpuDrivenHzbHeight_;
1044 for (uint32_t mip = 0; mip < gpuDrivenHzbOffsets_.size(); ++mip) {
1045 wgpu::ComputePassEncoder hzbPass = encoder.BeginComputePass();
1046 hzbPass.SetPipeline(gpuDrivenHzbPipeline_);
1047 const uint32_t dynamicOffset = mip * 256u;
1048 hzbPass.SetBindGroup(0, gpuDrivenHzbBindGroup_, 1, &dynamicOffset);
1049 hzbPass.DispatchWorkgroups((
width + 7u) / 8u, (
height + 7u) / 8u, 1);
1055 if (gpuDrivenComputePending_ && gpuDrivenCullPipeline_ && gpuDrivenComputeBindGroup_) {
1056 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1057 pass.SetPipeline(gpuDrivenCullPipeline_);
1058 pass.SetBindGroup(0, gpuDrivenComputeBindGroup_, 0,
nullptr);
1059 pass.DispatchWorkgroups((gpuDrivenDispatchCount_ + 63u) / 64u, 1, 1);
1061 gpuDrivenComputePending_ =
false;
1063 recordGpuDrivenVgCompute(encoder);
1066void Graphics::flushGpuDrivenDraws(wgpu::RenderPassEncoder pass,
bool canvasTarget,
bool hdrCanvas) {
1067 if (!gpuDrivenDrawPending_ ||
1068 (gpuDrivenResidentDrawPending_ ? !gpuDrivenResidentBuffer_ : !gpuDrivenVisibleBuffer_))
1070 auto& arena = currentUboArena();
1071 ensureUboArena(arena, arena.used + gpuDrivenBuckets_.size() * 2048);
1072 const wgpu::RenderPipeline& pipeline =
1073 gpuDrivenResidentDrawPending_
1074 ? (canvasTarget ? (hdrCanvas ? gpuDrivenResidentHdrCanvasPipeline_
1075 : gpuDrivenResidentCanvasPipeline_)
1076 : gpuDrivenResidentRenderPipeline_)
1077 : (canvasTarget ? (hdrCanvas ? gpuDrivenHdrCanvasPipeline_ : gpuDrivenCanvasPipeline_)
1078 : gpuDrivenRenderPipeline_);
1079 if (!pipeline)
return;
1080 pass.SetPipeline(pipeline);
1081 gpuDrivenLastIndirectDrawCount_ = 0;
1083 for (uint32_t i = 0; i < gpuDrivenBuckets_.size(); ++i) {
1084 const GpuDrivenBucket&
bucket = gpuDrivenBuckets_[i];
1085 auto* gpu =
static_cast<GpuMesh*
>(
bucket.mesh->gpuHandle);
1087 if (!gpu || !gpu->vertexBuffer || !gpu->indexBuffer || !
material)
continue;
1090 ubo.mvp = mesh3dViewProj;
1091 ubo.model = glm::mat4(1.f);
1092 ubo.lightDir = glm::vec4(glm::vec3(mesh3dLighting.
lights[0].
posRadius),
float(mesh3dLighting.
count));
1094 ubo.lightColor.w = mesh3dEnvIntensity;
1096 ubo.cameraPos = glm::vec4(mesh3dCameraPos,
material->getRoughness());
1097 ubo.ambient = glm::vec4(glm::vec3(mesh3dLighting.
ambient),
material->getMetallic());
1100 ubo.texBomb = glm::vec4(
material->getTexCellBombScale(),
material->getTexCellBombStrength(),
1101 material->getTexCellBombRotation(), 0.f);
1102 ubo.parallax = glm::vec4(
material->getParallaxScale(),
material->getParallaxMinLayers(),
1103 material->getParallaxMaxLayers(), 0.f);
1106 ubo.surface = glm::vec4(surfaceCode,
material->getAlphaCutoff(), ao,
1107 material->getCameraFacing() ? 1.f : 0.f);
1108 ubo.view = mesh3dView;
1109 ubo.clipInfo = glm::vec4(mesh3dNear, mesh3dFar, 0.f, 0.f);
1110 ubo.cloud = mesh3dCloud;
1111 ubo.cloudWind = mesh3dCloudWind;
1112 ubo.envProbeCenter = glm::vec4(mesh3dEnvProbeCenter, 1.f);
1113 ubo.envProbeExtent = glm::vec4(mesh3dEnvProbeExtent, 0.f);
1115 if (probeIndex >= mesh3dReflectionProbes.
count)
continue;
1116 const auto& probe = mesh3dReflectionProbes.
probes[probeIndex];
1117 GpuTexture* gpuProbe = gpuForTexture(probe.cubemap);
1118 if (!gpuProbe || !gpuProbe->isCube)
continue;
1119 ubo.reflectionProbeCenter[probeIndex] = glm::vec4(probe.center, probe.intensity);
1120 ubo.reflectionProbeExtent[probeIndex] = glm::vec4(probe.extent, probe.blendDistance);
1123 const uint32_t frameOffset = arena.alloc(
sizeof(Mesh3DUBO), 256);
1124 const uint32_t shadowOffset = arena.alloc(
sizeof(ShadowUBO), 256);
1125 queue.WriteBuffer(arena.buffer, frameOffset, &ubo,
sizeof(ubo));
1126 ShadowUBO shadow = mesh3dShadows.
ubo;
1128 queue.WriteBuffer(arena.buffer, shadowOffset, &shadow,
sizeof(shadow));
1130 GpuTexture*
depth = mesh3dSceneDepthTexture ? gpuForTexture(mesh3dSceneDepthTexture) : flatDepthTexture3D;
1131 wgpu::BindGroup bindGroup =
1132 makeMeshBindGroup(gpuForTexture(
material->getAlbedoTexture()), gpuForTexture(
material->getNormalTexture()),
1133 gpuForTexture(mesh3dEnvTexture), gpuForTexture(
material->getHeightTexture()),
depth,
1134 nullptr, frameOffset, shadowOffset, 0, uploadSkinPalette(
nullptr));
1135 const uint32_t offsets[3] = {frameOffset, shadowOffset, 0};
1136 pass.SetBindGroup(0, bindGroup, 3, offsets);
1137 WGPUBindGroupEntry modelEntry{};
1138 modelEntry.binding = 0;
1139 modelEntry.buffer = gpuDrivenResidentDrawPending_ ? gpuDrivenResidentBuffer_ : gpuDrivenVisibleBuffer_.Get();
1141 gpuDrivenResidentDrawPending_ ? gpuDrivenResidentOffset_ :
1142 uint64_t(
bucket.outputBase) *
sizeof(VisibleInstance);
1144 gpuDrivenResidentDrawPending_ ? gpuDrivenResidentSize_ :
1145 uint64_t(
bucket.inputCount) *
sizeof(VisibleInstance);
1146 WGPUBindGroupDescriptor modelDesc{};
1147 modelDesc.layout = gpuDrivenRenderSetLayout_.Get();
1148 modelDesc.entryCount = 1;
1149 modelDesc.entries = &modelEntry;
1150 wgpu::BindGroup modelGroup =
1151 device.CreateBindGroup(
reinterpret_cast<const wgpu::BindGroupDescriptor*
>(&modelDesc));
1152 pass.SetBindGroup(1, modelGroup, 0,
nullptr);
1153 pass.SetVertexBuffer(0, gpu->vertexBuffer, 0, gpu->vertexCount * 32ull);
1154 const uint64_t indexBytes = gpu->indexFormat == wgpu::IndexFormat::Uint16 ? 2u : 4u;
1155 pass.SetIndexBuffer(gpu->indexBuffer, gpu->indexFormat, 0, uint64_t(gpu->indexCount) * indexBytes);
1156 pass.DrawIndexedIndirect(gpuDrivenIndirectBuffer_, uint64_t(i) *
sizeof(
GpuIndirectCommand));
1157 ++gpuDrivenLastIndirectDrawCount_;
1159 gpuDrivenDrawPending_ =
false;
1160 gpuDrivenResidentDrawPending_ =
false;
1161 gpuDrivenResidentBuffer_ =
nullptr;
1162 gpuDrivenResidentOffset_ = 0;
1163 gpuDrivenResidentSize_ = 0;
1164 gpuDrivenBuckets_.clear();
std::unordered_map< std::string, QuestRuntime > entries
std::vector< BuildingInstanceSnapshot > instances
std::uint32_t vertexCount
std::uint32_t firstVertex
std::uint32_t firstInstance
std::uint32_t paddedCount
std::uint32_t instanceCount
wgpu::PopErrorScopeStatus status
std::array< float, 3 > scale
const UnitySourceAsset & source
std::vector< VegetationPresetCommand > commands
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
EVENGINE_API_FOUNDATION public API.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
std::unique_ptr< RenderControl > renderControl_
Packages shading method + surface parameters into one attachable asset.
GPU mesh handle (+ optional CPU morph targets).
void gpuDrivenCullEmit(const glm::mat4 &viewProj, const glm::vec3 &eye, float fovYDeg, float nearZ, float farZ) override
Gpu driven cull emit.
void gpuDrivenDrawOpaque() override
Gpu driven draw opaque.
uint32_t gpuDrivenMaterialRecord(Material *material) override
Gpu driven material record.
bool gpuDrivenCullBegin(const GpuInstance *instances, uint32_t instanceCount) override
Gpu driven cull begin.
bool gpuDrivenMaterialUsable(Material *material) override
Gpu driven material usable.
uint32_t gpuDrivenMeshRecord(Mesh *mesh) override
Gpu driven mesh record.
Result< void > gpuDrivenReleaseMaterialRecord(Material *material) override
Gpu driven release material record.
GpuResidentSubmitStatus gpuDrivenSubmitResident(const GpuResidentInstanceBatch &batch) override
Gpu driven submit resident.
bool gpuDrivenSubmitOpaque(const GpuInstance *instances, uint32_t instanceCount) override
Gpu driven submit opaque.
uint32_t debugGpuDrivenGpuVisibleCount()
Read back the last GPU-written indirect instance total (native tests).
std::vector< ParamSpec > params
eve::graphics::GpuIndirectCommand GpuIndirectCommand
constexpr uint32_t kMaxHzbMips
eve::graphics::GpuInstance GpuInstance
const char * kMesh3DFragWgsl
constexpr uint32_t kInvalidGpuDrivenSlot
GPU-driven rendering shared constants + std430 GPU layouts.
GpuResidentSubmitStatus
Structured result for direct resident-instance submission.
std::vector< int64_t > attrs(const Node &n, const char *key, std::vector< int64_t > fallback)
Attrs.
constexpr uint64_t kGpuResidentStorageOffsetAlignment
Portable alignment required for resident storage-buffer slice offsets.
GpuResidentBackend backend
Indirect draw command; layout identical to VkDrawIndexedIndirectCommand.
Per-instance GPU record (std430). Mirrors GLSL GpuInstance.
Direct-render description for a GPU-authored array of GpuInstance records. @ownership buckets and buf...
GpuResidentBufferView buffer
const GpuResidentInstanceBucket * buckets
One contiguous mesh/material bucket in a sorted resident instance buffer.
static constexpr int kMaxLights
Light3DGpu lights[kMaxLights]
static constexpr int kMaxProbes
Vertex/index buffers for one mesh.
std::future< vkb::Instance > future
glm::vec4 terrainWaveTint