npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

webgpu-simplified

v0.1.34

Published

A collection of helper functions to simplify the process of building WebGPU applications.

Downloads

10

Readme

WebGPU is a work-in-progress graphics API and future web standard for graphics and compute. The intention of the webgpu-simplified package is to simplify the process of building WebGPU apps. It is a side project that has come out of the examples included in my soon-to-be-published book "WebGPU by Examples". It is not a renderer nor a render engine, but simply a collection of helper functions and interfaces. Unlike a render engine, this mini library does not alter the code structure and workflow of the original WebGPU applications.

The helper functions contained in this package can help you build WebGPU apps quickly and avoid code duplication in creating GPU buffers, render/compute pipelines, render pass, and 3D transformations. This mini library does not do everything, and if you want add more features to it, you can do so by pulling the GitHub repo. In certain cases where a particular feature may not be implemented in the package, you can always write standard WebGPU code for it, which is much more flexible than a render engine.

Usage

Install the library by running

npm install webgpu-simplified

You can import all functions and interfaces

import * as ws from "webgpu-simplified"

or just import the bits you need

import { 
    createRenderPipelineDescriptor,
    createBufferWithData,
    createBindGroup,
    ...
} from "webgpu-simplified"

Descriptions about Functions

The detailed explanation about the functions included in the package can be found here.

Examples

Simplify render-pipeline-descriptor creation

Supporse we have the following render pipeline descriptor written in standard WebGPU code:

const descriptor = {
    layout: 'auto',
    vertex: {
        module: device.createShaderModule({                    
            code: shader
        }),
        entryPoint: "vs_main",
        buffers:[
            {
                arrayStride: 12,  // position
                attributes: [{
                    shaderLocation: 0,
                    format: "float32x3",
                    offset: 0
                }]
            },
            {
                arrayStride: 12,  // normal
                attributes: [{
                    shaderLocation: 1,
                    format: "float32x3",
                    offset: 0
                }]
            },
            {
                arrayStride: 8,   // uv
                attributes: [{
                    shaderLocation: 2,
                    format: "float32x2",
                    offset: 0
                }]
            }
        ]
    },
    fragment: {
        module: device.createShaderModule({                    
            code: shader
        }),
        entryPoint: "fs_main",
        targets: [
            {
                format: navigator.gpu.getPreferredCanvasFormat()
            }
        ]
    },
    primitive:{
        topology: "triangle-list",
    },
    depthStencil:{
        format: "depth24plus",
        depthWriteEnabled: true,
        depthCompare: "less"
    }
}

The buffers attribute in the vertex stage contains position, normal, and uv data at the shaderLocation 0, 1, and 2 respectively. We can simplify this code using webgpu-simplified with the following code:

import * as ws from "webgpu-simplified";

let bufs = ws.setVertexBuffers(['float32x3', 'float32x3', 'float32x2]);
const descriptor = ws.createRenderPipelineDescriptor({
    init, shader,
    buffers: bufs,
});

This greatly simplifies the original WebGPU code for creating the same render pipeline descriptor.

Simplify render-pass-descriptor creation

Suppose we have the following render pass descriptor written in the standard WebGPU code:

const descriptor = {
    colorAttachments: [{
        view: gpuTexture.createView(),
        resolveTarget: msaaTexture.createView(),
        clearValue: { r: 0.2, g: 0.247, b: 0.314, a: 1.0 },
        loadOp: 'clear',
        storeOp: 'store'
    }],
    depthStencilAttachment: {
        view: depthTexture.createView(),
        depthClearValue: 1.0,
        depthLoadOp: 'clear',
        depthStoreOp: "store",
    }
};

This code is used in the cases where MSAA sample count = 4.

We can simplify this code using webgpu-simplified with the following code:

const descriptor = ws.createRenderPassDescriptor({
    init,
    depthView: depthTexture.createView(),
    textureView: gpuTexture.createView(),
});

Simplify bind-group creation

Here is the standard WebGPU code for creating a uniform bind group:

const uniformBindGroup = device.createBindGroup({
    layout: pipeline.getBindGroupLayout(0),
    entries: [
        {
            binding: 0,
            resource: {
                buffer: vertexUniformBuffer,
                offset: 0,
                size: 192
            }
        },
        {
            binding: 1,
            resource: {
                buffer: fragmentUniformBuffer,
                offset: 0,
                size: 32
            }
        },
        {
            binding: 2,
            resource: {
                buffer: lightUniformBuffer,
                offset: 0,
                size: 48
            }
        }   
        {
            binding: 3,
            resource: ts.sampler
        },
        {
            binding: 4,
            resource: ts.texture.createView()
        }                                   
    ]
});

We can simplify this code using webgpu-simplified with the following code:

import * as ws from "webgpu-simplified";

const uniformBuffers = [vertexUniformBuffer, fragmentUniformBuffer, lightUniformBuffer];
const otherBindingResources = [ts.sampler, ts.texture.createView()];

const uniformBindGroup = ws.createBindGroup(device, pipeline.getBindGroupLayout(0), 
    uniformBuffers, otherBindingResources);

Update vertex buffers

If varying some parameters causes changes in the vertex data and GPU buffer size (e.g. changing the radius and u-v segments in a UV sphere example), we need to update the vertex buffers. Here is the standard WebGPU code for doing it:

const updateBuffers = (origNumVertices:number) => {
    if(posData.length === origNumVertices){
        // buffer size not changed, we can write new data to original buffers:
        device.queue.writeBuffer(posBuffer, 0, posData);
        device.queue.writeBuffer(normalBuffer, 0, normalData);
        device.queue.writeBuffer(uvBuffer, 0, uvData);
        device.queue.writeBuffer(indexBuffer, 0, indexData);
    } else {
        // buffer size changed, we must recreate buffers with new data:
        posBuffer.destroy();
        normalBuffer.destroy();
        uvBuffer.destroy();
        indexBuffer.destroy();

        posBuffer = device.createBuffer({
            size: posData.byteLength,
            usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
        });
        normalBuffer = device.createBuffer({
            size: normalData.byteLength,
            usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
        });
        uvBuffer = device.createBuffer({
            size: uvData.byteLength,
            usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
        });
        indexBuffer = device.createBuffer({
            size: indexData.byteLength,
            usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
        });
        device.queue.writeBuffer(posBuffer, 0, posData);
        device.queue.writeBuffer(normalBuffer, 0, normalData);
        device.queue.writeBuffer(uvBuffer, 0, uvData);
        device.queue.writeBuffer(indexBuffer, 0, indexData);
    }
}

We can simplify this code using webgpu-simplified with the following code:

import * as ws from 'webgpu-simplified';

const updateBuffers = (origNumVertices:number) => {
    const data = [posData, normalData, uvData, indexData];
    ws.updateVertexBuffers(device, pipeline, data, origNumVertices);
}

Create GPU buffers with data

We can create a buffer with data initialize it using the following standard WebGPU code:

// create a vertex buffer
const createVertexBuffer = (data: Float32Array):GPUBuffer => {
    const buffer = device.createBuffer({
        size: data.byteLength,
        usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
        mappedAtCreation: true,
    });
    new Float32Array(buffer.getMappedRange()).set(data);
    buffer.unmap();
    return buffer;
}

// create an index buffer
const createIndexBuffer = (data: Uint32Array):GPUBuffer => {
    const buffer = device.createBuffer({
        size: data.byteLength,
        usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
        mappedAtCreation: true,
    });
    new Uint32Array(buffer.getMappedRange()).set(data);
    buffer.unmap();
    return buffer;
}

// create a uniform buffer
const createUniformBuffer = (data: Float32Array):GPUBuffer => {
    const buffer = device.createBuffer({
        size: data.byteLength,
        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
        mappedAtCreation: true,
    });
    new Float32Array(buffer.getMappedRange()).set(data);
    buffer.unmap();
    return buffer;
}

// create a storage buffer
const createStorageBuffer = (data: Float32Array):GPUBuffer => {
    const buffer = device.createBuffer({
        size: data.byteLength,
        usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | 
               GPUBufferUsage.COPY_SRC,
        mappedAtCreation: true,
    });
    new Float32Array(buffer.getMappedRange()).set(data);
    buffer.unmap();
    return buffer;
}

We can simplify this code using webgpu-simplified with the following code:

import * as ws from 'webgpu-simplified';

// create a vertex buffer using data with a type of Float32Array:
const vertexBuffer = ws.createBufferWithData(device, data);

// create an index buffer using data with a type of Uint32Array:
const indexBuffer = ws.createBufferWithData(device, data);

// create a uniform buffer using data with a type of Float32Array:
const uniformBuffer = ws.createBufferWithData(device, data, ws.BufferType.Uniform);

// create a storage buffer using data with a type of Float32Array:
const uniformBuffer = ws.createBufferWithData(device, data, ws.BufferType.Storage); 

Transformations

Like WebGL, WebGPU does not provide any functions for working with model, view, and projection transformations. In this mini library, I implement several helper functions for generating varous 3D transformations using a popular JavaScript package gl-matrix.

Here is the sample code for creating various transformations:

import * as ws from 'webgpu-simplified';

// create a model matrix using translation, rotation, and scale:
const modelMat = ws.createModelMat(translation, rotation, scale);

// create a view matrix and a camera
const vt = ws.createViewTransform(cameraPosition);
const viewMat = vt.viewMat;
let camera = ws.getCamera(canvas, vt.cameraOptions);

// create a projection matrix:
const projectionMat = ws.createProjectionMat(aspectRatio);

// combine model, view, and projection matrices to form mvp matrix:
const mvpMat = ws.combineMvpMat(modelMat, viewMat, projectionMat);

You can see that our package also include a getCamera function based on a npm library 3d-view-controls. This function let you create an easy to use camera that allows you to interact with graphics objects in the scene using mouse, such as pan, rotate, and zoom in/out the objects.

Utility

This package also includes some utility functions.

Contribution, Suggestions

I accept pull requests (GitHub repo) for fixing issue or sharing your own helper functions on WebGPU with others. Any suggestions and new feature requests would be welcomed and much appreciated.

License

Copyright (c) 2023 Dr. Jack Xu. MIT License.