Bloom / HDR post-process primitives for the WebGPU backend #27
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Catcrafts/Crafter.Graphics#27
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Downstream (3DForts #130) wants a bloom effect — an HDR post-process chain (threshold → downsample → blur → upsample → composite+tonemap) running on both backends. Auditing the framework, the Vulkan path can already express this, but the WebGPU backend is missing the primitives an HDR post chain needs. This issue tracks the minimal upstream surface to unblock a cross-backend HDR post-process.
What already works (no change needed)
:PlainComputeShader(WebGPU) /:ComputeShader(Vulkan), dispatched via customRenderPasssubclasses pushed ontowindow.passes. A multi-pass post chain is expressible.RESOLVEwavefront stage (WebGPURTStage::Resolve,resolve_main(coord, hdr) -> vec4) — a per-pixel tonemap/gamma hook, used by theRTStress/Sponzaexamples.Image2D<T>aliasesImageVulkan<T>with fullVkFormat(e.g.R16G16B16A16_SFLOAT), storage usage, mip levels, andSamplerVulkanfiltering. The RT pipeline writes a heap-bound output slot, so an offscreen HDR target + a compositeRenderPassis wireable without framework changes. (Worth confirming the present path doesn't assume RT/UI writes the swapchain directly — see ask 4.)Gaps (WebGPU backend)
1. No float/HDR GPU textures
Every texture-creation bridge import hardcodes its format and is CPU-upload-only:
WebGPU::wgpuCreateImage2D(w, h)→rgba8unorm(Crafter.Graphics-WebGPU.cppm)wgpuCreateImage2DArray(...)→rgba8unormlayerswgpuCreateAtlasTexture(...)→r8unormImage2D<T>(DOM) is "a thin handle around an rgba8unorm GPUTexture; the only update path is from a CompressedTextureAsset" (Crafter.Graphics-Image2D.cppm).There is no way to allocate an
rgba16float(or any non-rgba8unorm) texture at runtime, and no usage flags forSTORAGE_BINDING/RENDER_ATTACHMENT.Ask: a runtime texture-create path that takes a format (at least
rgba16float) and theSTORAGE_BINDING | TEXTURE_BINDINGusages, with noCompressedTextureAssetupload requirement.2. No writable storage-texture binding for user textures
UICustomBindingKind(Crafter.Graphics-WebGPUComputeShader.cppm) isBuffer / BufferReadWrite / SampledTexture / SampledTextureArray / Sampler. The only writable texture a compute dispatch gets is the library-managedrgba8unormping-pong pair at@group(1)(out: texture_storage_2d<rgba8unorm, write>,prev: texture_2d<f32>).A bloom mip pyramid needs N distinct writable float targets at different resolutions, bound at arbitrary
@group/@binding— the single fixed ping-pong pair can't express it.Ask: a
StorageTexture(write)UICustomBindingKindcarrying a texel format, so a compute pass can write a user-ownedrgba16floattexture at a user-chosen group/binding; and sampling of float textures via the existingSampledTexturekind (sample typefloat, filterable —rgba16floatis filterable per spec).3. No access to pre-tonemap linear radiance
The wavefront's linear accumulator (
wfAccum,array<vec4<f32>>per pixel — seeWAVEFRONT-DESIGN.md) is internal, andRESOLVEcollapses it directly to thergba8unormout image. There's no HDR buffer a downstream pass can threshold/blur, andresolve_mainruns after the post chain would need its input.Ask: one of —
RESOLVEwrite to a user-providedrgba16floattarget instead of thergba8unormout image (the app then owns the final composite→swapchain pass), orwfAccumas a sampleable texture / readable buffer to user passes, orPipelineRTWebGPUrender its out image to a user-provided HDR texture rather than the canvas, so the app's own compositeRenderPassdoes tonemap+gamma+present.(a) is the smallest change and keeps the existing stage model.
4. Symmetric Vulkan confirmation
Confirm the Vulkan present path supports: RT → offscreen
rgba16floatheap image, then a user compositeRenderPassthat writes the swapchain. The heapoutImageSlotindirection suggests this already works; flagging in case the present/acquire path assumes the final RT/UI pass targets the swapchain image directly.Minimal surface that unblocks bloom
rgba16floattexture create with storage+sampled usage (gap 1).StorageTexturewrite binding kind w/ format + float-texture sampling (gap 2).With these, the bloom chain is identical-shaped on both backends: RT → linear
rgba16float→ threshold/down/up compute passes over float mip targets → composite (tonemap+gamma) → swapchain.Filed from a 3DForts #130 (bloom) scoping pass. File refs are against the build-cache-pinned commit; the
mastercheckout predates the WebGPU/wavefront backend.