{"id":21703547,"url":"https://github.com/jolifantobambla/webgpu-spd","last_synced_at":"2025-04-12T15:12:19.758Z","repository":{"id":234980916,"uuid":"784739158","full_name":"JolifantoBambla/webgpu-spd","owner":"JolifantoBambla","description":"A port of AMD's Single Pass Downsampler for WebGPU","archived":false,"fork":false,"pushed_at":"2024-06-20T14:51:11.000Z","size":131,"stargazers_count":16,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-12T15:12:08.536Z","etag":null,"topics":["gpu","graphics","mipmap","webgpu"],"latest_commit_sha":null,"homepage":"https://jolifantobambla.github.io/webgpu-spd/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JolifantoBambla.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-10T13:10:53.000Z","updated_at":"2025-01-19T16:14:17.000Z","dependencies_parsed_at":"2024-06-21T06:45:11.660Z","dependency_job_id":"e8ad2eef-7f95-465e-8291-9b9d8bf41882","html_url":"https://github.com/JolifantoBambla/webgpu-spd","commit_stats":{"total_commits":38,"total_committers":1,"mean_commits":38.0,"dds":0.0,"last_synced_commit":"18f82fe21fc2fb52c9be9d18934d31ff97a3dfa3"},"previous_names":["jolifantobambla/webgpu-spd"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JolifantoBambla%2Fwebgpu-spd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JolifantoBambla%2Fwebgpu-spd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JolifantoBambla%2Fwebgpu-spd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JolifantoBambla%2Fwebgpu-spd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JolifantoBambla","download_url":"https://codeload.github.com/JolifantoBambla/webgpu-spd/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248586245,"owners_count":21128998,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gpu","graphics","mipmap","webgpu"],"created_at":"2024-11-25T21:33:20.423Z","updated_at":"2025-04-12T15:12:19.738Z","avatar_url":"https://github.com/JolifantoBambla.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WebGPU SPD\n\nA utility library for generating up to 12 mip levels for 2d textures \u0026 texture arrays in a single WebGPU compute pass.\n\n## Docs\n\nFind the docs [here](https://jolifantobambla.github.io/webgpu-spd).\n\nTry it out [here](https://jolifantobambla.github.io/webgpu-spd/demo).\n\n## Installation\n\n### NPM\n```bash\nnpm install webgpu-spd\n```\n\n### From GitHub\n```js\nimport { WebGPUSinglePassDownsampler } from 'https://jolifantobambla.github.io/webgpu-spd/2.x/dist/index.js';\n```\n\n### From UNPKG\n```js\nimport { WebGPUSinglePassDownsampler } from 'https://unpkg.com/webgpu-spd@2.0.0/dist/index.js';\n```\n\n## Usage\n\nWebGPU SPD downsamples 2d textures and 2d texture arrays using compute pipelines generating up to 12 mip levels in a single pass (all array layers are processed in the same pass). The maximum number of mip levels that can be generated within a single pass depends on the `maxStorageTexturesPerShaderStage` limit supported by the device used.\nShould the number of mip levels requested for a texture exceed this limit, multiple passes, generating up to `min(maxStorageTexturesPerShaderStage, 12)` mip levels each, will be used instead.\nThe mip levels generated for a given input texture are stored either in the input texture or in a separate target texture if specified.\nThis output texture must support `GPUTextureUsage.STORAGE_BINDING` with access mode `\"write-only\"`.\n\n#### Generate mipmaps\n```js\nimport { WebGPUSinglePassDownsampler, maxMipLevelCount } from 'webgpu-spd';\n\nconst downsampler = new WebGPUSinglePassDownsampler();\n\nconst size = [/* size + array layers */];\nconst texture = device.createTexture({\n    size,\n    mipLevelCount: maxMipLevelCount(size[0], size[1]),\n    format: 'rgba8unorm',\n    usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,\n});\n\n// write mip level 0\n\ndownsampler.generateMipmaps(device, texture);\n```\n\n#### Downsample a texture each frame\n```js\nimport { WebGPUSinglePassDownsampler, SPDFilters } from 'webgpu-spd';\n\n// during setup\nconst downsampler = new WebGPUSinglePassDownsampler();\nconst downsampleDepthPass = downsampler.preparePass(device, linearDepthTexture, { filter: SPDFilters.Min }); \n\n// in render loop\nconst commandEncoder = device.createCommandEncoder();\n\nconst computePassEncoder = commandEncoder.beginComputePass();\ndownsampleDepthPass.encode(computePassEncoder);\ncomputePassEncoder.end();\n\ndevice.queue.submit([commandEncoder.finish()]);\n```\n\n#### Downsample into target\n```js\nimport { WebGPUSinglePassDownsampler, maxMipLevelCount } from 'webgpu-spd';\n\nconst downsampler = new WebGPUSinglePassDownsampler();\n\nconst size = [/* width, height, array layers */];\nconst texture = device.createTexture({\n    size,\n    mipLevelCount: 1,\n    format: 'rgba8unorm',\n    usage: GPUTextureUsage.TEXTURE_BINDING,\n});\nconst target = device.createTexture({\n    size: [size[0] / 2, size[1] / 2, size[2]],\n    mipLevelCount: maxMipLevelCount(size[0], size[1]) - 1,\n    format: 'rgba8unorm',\n    usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,\n});\n\n// write mip level 0\n\ndownsampler.generateMipmaps(device, texture, { target });\n```\n\n#### Use min-max filter to generate a min-max pyramid for single-channel textures\n\nThe `SPDFilters.MinMax` filter provided by WebGPU SPD is a special filter that is meant to be used with input textures using single-channel formats like `\"r32float\"`, and a target texture using a two-channel format like `\"rg32float\"`.\nAfter the downsampling pass, the target texture will contain the minimum values in the red channel and the maximum values in the green channel.\n\n```js\nimport { WebGPUSinglePassDownsampler, SPDFilters, maxMipLevelCount } from 'webgpu-spd';\n\n// during setup\nconst downsampler = new WebGPUSinglePassDownsampler();\nconst linearDepth = device.createTexture({\n    size: [/* gBuffer size */],\n    mipLevelCount: 1,\n    format: 'r32float',\n    usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,\n});\nconst minMaxDepthPyramid = device.createTexture({\n    size: [linearDepth.width / 2, linearDepth.height / 2],\n    mipLevelCount: maxMipLevelCount(linearDepth.width, linearDepth.height) - 1\n    format: 'rg32float',\n    usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,\n});\nconst minMaxDepthPass = downsampler.preparePass(device, linearDepth, {\n    target: minMaxDepthPyramid,\n    filter: SPDFilters.MinMax,\n}); \n\n// in render loop\n\n// ... write mip level 0 of linearDepth\n\nminMaxDepthPass.encode(computePassEncoder);\n```\n\n#### Prepare pipelines for expected formats\n\nIn the above examples, GPU resources, like compute pipelines and bind group layouts etc., are created on the fly the first time a new configuration of `GPUDevice`, `GPUTextureFormat`, filter, and precision is needed.\n\nWebGPU SPD also supports allocating resources during setup, like this:\n\n```js\nimport { WebGPUSinglePassDownsampler, SPDFilters, SPDPrecision } from 'webgpu-spd';\n\nconst downsampler = new WebGPUSinglePassDownsampler({ device, formats: [\n    { format: 'rgba8unorm', halfPrecision: true },\n    { format: 'r32float', filters: [ SPDFilters.Min ] },\n]});\n\n// alternatively call\ndownsampler.prepareDeviceResources({ device, formats: [\n    { format: 'rgba8unorm', halfPrecision: true },\n    { format: 'r32float', filters: [ SPDFilters.Min ] },\n]});\n```\n\n#### Limit the number of mip levels and array layers per pass\n\nGenerating more than 6 mip levels per pass might not be supported on each platform due to buffers being not coherent by default yet.\nWebGPU SPD uses `min(device.limits.maxStorageTexturesPerPass, 12)` by default and can thus be implicitly configured using the device's limit.\nHowever, this might not be desirable in all cases, so WebGPU SPD can be configured to use a different limit by setting the corresponding option when preparing device resources.\n\nIf more than 6 mip levels are downsampled per pass, WebGPU SPD allocates additional internal resources to store intermediate texture data (`16 * 64 * 64 * maxArrayLayersPerPass` bytes) and for control flow purposes (`4 * maxArrayLayersPerPass` bytes).\nThe size of these resources depends on the number of array layers that can be downsampled each pass.\nIf a texture's number of array layers exceeds the number of array layers per pass, multiple passes will be used instead.\nBy default, WebGPU SPD uses the device's `maxTextureArrayLayers` limit.\n\nWebGPU SPD can be configured to use different limits like this:\n\n```js\nimport { WebGPUSinglePassDownsampler, SPDFilters } from 'webgpu-spd';\n\nconst downsampler = new WebGPUSinglePassDownsampler({ device, maxMipsPerPass: 6, maxArrayLayersPerPass: 1 });\n\n// alternatively call\ndownsampler.prepareDeviceResources({ device, maxMipsPerPass: 6, maxArrayLayersPerPass: 1 });\n```\n\n#### Handling device loss\n```js\nimport { WebGPUSinglePassDownsampler, SPDFilters } from 'webgpu-spd';\n\nconst formatConfigs = [\n    { format: 'rgba8unorm' },\n    { format: 'r32float', filters: [ SPDFilters.Min ] },\n];\n\n// on new device\ndownsampler.deregisterDevice(oldDevice);\ndownsampler.prepareDeviceResources({ device: newDevice, formats: formatConfig s});\ndownsampleTexturePass = downsampler.preparePass(newDevice, texture);\n```\n\n#### Use custom filters\n\nCustom filters for downsampling a quad to a single pixel can be registered with WebGPU SPD using `registerFilter`.\nThe given WGSL code must at least define a reduction function with the following name and signature:\n\n```wgsl\nfn spd_reduce_4(v0: vec4\u003cSPDScalar\u003e, v1: vec4\u003cSPDScalar\u003e, v2: vec4\u003cSPDScalar\u003e, v3: vec4\u003cSPDScalar\u003e) -\u003e vec4\u003cSPDScalar\u003e\n```\n\nIf a filter is known to be only used with a single scalar type (e.g., `u32`), uses of `SPDScalar` can also be replaced by that scalar type.\n\nFor example, a custom filter that only takes a single pixel value out of the four given ones could be implemented and used like this:\n\n```js\nimport { WebGPUSinglePassDownsampler } from 'webgpu-spd';\n\nconst downsampler = new WebGPUSinglePassDownsampler();\ndownsampler.registerFilter('upperLeft', `\n    fn spd_reduce_4(v0: vec4\u003cSPDScalar\u003e, v1: vec4\u003cSPDScalar\u003e, v2: vec4\u003cSPDScalar\u003e, v3: vec4\u003cSPDScalar\u003e) -\u003e vec4\u003cSPDScalar\u003e {\n        return v0;\n    }\n`);\n\n// ...\n\ndownsampler.generateMipmaps(device, texture, { filter: 'upperLeft' });\n```\n\n#### Downsample image region\n\n```js\nimport { WebGPUSinglePassDownsampler } from 'webgpu-spd';\n\nconst downsampler = new WebGPUSinglePassDownsampler();\n\nconst sizeHalf = [texture.width / 2, texture.height / 2];\ndownsampler.generateMipmaps(device, texture, { offset: sizeHalf, size: sizeHalf});\n```\n\n## Contributions\n\nContributions are very welcome. If you find a bug or think some important functionality is missing, please file an issue [here](https://github.com/JolifantoBambla/webgpu-spd/issues). If want to help out yourself, feel free to submit a pull request [here](https://github.com/JolifantoBambla/webgpu-spd/pulls).\n\n## Acknowledgements\n\nThis library is a WebGPU port of the FidelityFX Single Pass Downsampler (SPD) included in AMD's [FidelityFX-SDK](https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjolifantobambla%2Fwebgpu-spd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjolifantobambla%2Fwebgpu-spd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjolifantobambla%2Fwebgpu-spd/lists"}