This commit is contained in:
Rowan 2025-04-22 21:39:07 -05:00
parent 81f2d118a0
commit 0763a35a65
15 changed files with 136 additions and 448 deletions

View file

@ -6,6 +6,6 @@ await build({
entryPoints: ['index.js'],
bundle: true,
outfile: './dist/index.js',
plugins: [wgsl({ filterWith: true, filterExtension: false })]
plugins: [wgsl()]
})

153
dist/index.js vendored
View file

@ -7196,158 +7196,6 @@
}
};
// src/utils/mip-shader.wgsl
var mip_shader_default = "var<private> pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -1.0), vec2<f32>(-1.0, 3.0), vec2<f32>(3.0, -1.0));\n\nstruct VertexOutput {\n @builtin(position) position : vec4<f32>,\n @location(0) texCoord : vec2<f32>,\n};\n\n@vertex\nfn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {\n var output : VertexOutput;\n output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);\n output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);\n return output;\n}\n\n@group(0) @binding(0) var imgSampler : sampler;\n@group(0) @binding(1) var img : texture_2d<f32>;\n\n@fragment\nfn fragmentMain(@location(0) texCoord : vec2<f32>) -> @location(0) vec4<f32> {\n return textureSample(img, imgSampler, texCoord);\n}\n";
// src/utils/mip-generator.ts
var mip = (n2) => Math.max(1, n2 >>> 1);
var MipGenerator = class {
constructor(device) {
this._device = device;
this._sampler = device.createSampler({ minFilter: "linear" });
this._pipelines = {};
}
_getShader() {
if (!this._shader) {
this._shader = this._device.createShaderModule({
code: mip_shader_default
});
}
return this._shader;
}
_getBindGroupLayout() {
if (!this._bindGroupLayout) {
this._bindGroupLayout = this._device.createBindGroupLayout({
entries: [{
binding: 0,
visibility: GPUShaderStage.FRAGMENT,
sampler: {}
}, {
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
texture: {}
}]
});
}
return this._bindGroupLayout;
}
_getPipelineLayout() {
if (!this._pipelineLayout) {
this._pipelineLayout = this._device.createPipelineLayout({
label: "Mipmap Generator",
bindGroupLayouts: [this._getBindGroupLayout()]
});
}
return this._pipelineLayout;
}
getPipeline(format) {
let pipeline = this._pipelines[format];
if (!pipeline) {
const shader = this._getShader();
pipeline = this._device.createRenderPipeline({
layout: this._getPipelineLayout(),
vertex: {
module: shader,
entryPoint: "vs_main"
},
fragment: {
module: shader,
entryPoint: "fs_main",
targets: [{ format }]
}
});
this._pipelines[format] = pipeline;
}
return pipeline;
}
generateMipmap(texture, descriptor) {
const pipeline = this.getPipeline(descriptor.format);
if (descriptor.dimension !== "2d") {
throw new Error("Generating mipmaps for anything except 2d is unsupported.");
}
let mipTexture = texture;
const { width, height, depthOrArrayLayers } = descriptor.size;
const renderToSource = descriptor.usage & GPUTextureUsage.RENDER_ATTACHMENT;
if (!renderToSource) {
mipTexture = this._device.createTexture({
size: {
width: mip(width),
height: mip(height),
depthOrArrayLayers
},
format: descriptor.format,
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
mipLevelCount: descriptor.mipLevelCount - 1
});
}
const encoder = this._device.createCommandEncoder({});
for (let layer = 0; layer < depthOrArrayLayers; ++layer) {
let srcView = texture.createView({
baseMipLevel: 0,
mipLevelCount: 1,
dimension: "2d",
baseArrayLayer: layer,
arrayLayerCount: 1
});
let dstMipLevel = renderToSource ? 1 : 0;
for (let i2 = 1; i2 < descriptor.mipLevelCount; ++i2) {
const dstView = mipTexture.createView({
baseMipLevel: dstMipLevel++,
mipLevelCount: 1,
dimension: "2d",
baseArrayLayer: layer,
arrayLayerCount: 1
});
const passEncoder = encoder.beginRenderPass({
colorAttachments: [{
view: dstView,
loadOp: "clear",
storeOp: "store"
}]
});
const bindGroup = this._device.createBindGroup({
layout: this._bindGroupLayout,
entries: [{
binding: 0,
resource: this._sampler
}, {
binding: 1,
resource: srcView
}]
});
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.draw(3, 1, 0, 0);
passEncoder.end();
srcView = dstView;
}
}
if (!renderToSource) {
const mipLevelSize = {
width: mip(width),
height: mip(height),
depthOrArrayLayers
};
for (let i2 = 1; i2 < descriptor.mipLevelCount; ++i2) {
encoder.copyTextureToTexture({
texture: mipTexture,
mipLevel: i2 - 1
}, {
texture,
mipLevel: i2
}, mipLevelSize);
mipLevelSize.width = mip(mipLevelSize.width);
mipLevelSize.height = mip(mipLevelSize.height);
}
}
this._device.queue.submit([encoder.finish()]);
if (!renderToSource) {
mipTexture.destroy();
}
return texture;
}
};
// index.js
async function main() {
const canvas = (
@ -7361,7 +7209,6 @@
canvas.width = 800;
canvas.height = 600;
const graphicsDevice = await GraphicsDevice.build().withCanvas(canvas).withAdapter({ powerPreference: PowerPreference.HighPerformance }).build();
const mipGenerator = new MipGenerator(graphicsDevice.device);
const success = await graphicsDevice.initialize();
if (!success) {
console.error("Failed to initialize WebGPU.");

View file

@ -1,6 +1,5 @@
import { GraphicsDevice } from './src/core/graphics-device.js'
import { PowerPreference, VertexFormat } from './src/enum.js'
import { MipGenerator } from './src/utils/mip-generator.js'
async function main() {
const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('webgpu-canvas'))
@ -14,11 +13,9 @@ async function main() {
canvas.height = 600
const graphicsDevice = await GraphicsDevice.build()
.withCanvas(canvas)
const graphicsDevice = await GraphicsDevice.build(canvas)
.withAdapter({ powerPreference: PowerPreference.HighPerformance })
.build()
const mipGenerator = new MipGenerator(graphicsDevice.device)
.finish()
const success = await graphicsDevice.initialize()
@ -119,22 +116,11 @@ async function main() {
const pipeline = graphicsDevice.createRenderPipeline(pipelineDescriptor)
/** @type {Array<import('./src/core/graphics-device.js').BindGroupEntry>} */
const uniformBindings = [{
binding: 0,
resource: uniformBuffer
}]
const uniformBindGroup = material.createBindGroup(
0,
{ transform: uniformBuffer },
'Uniforms'
)
//const uniformBindGroup = graphicsDevice.createBindGroup(
// material.bindGroupLayouts[0],
// uniformBindings,
// 'Uniforms'
//)
async function frame() {
if (!graphicsDevice.isInitialized) {

View file

@ -35,7 +35,7 @@ export class CommandRecorder {
}]
}
beginRenderPass(colorAttachments: GPURenderPassColorAttachment[], depthStencilAttachment: GPURenderPassDepthStencilAttachment): GPURenderPassEncoder {
beginRenderPass(colorAttachments?: GPURenderPassColorAttachment[], depthStencilAttachment?: GPURenderPassDepthStencilAttachment): GPURenderPassEncoder {
if (this._passEncoder) {
throw CommandRecorderError.activeRenderPass()
}

View file

@ -3,7 +3,7 @@ import { Buffer, UniformBuffer } from '../resources/buffer.js'
import { GraphicsDeviceError, WebGPUError, BufferError, WebGPUObjectError } from '../utils/errors.js'
import { GraphicsDeviceInitialized, GraphicsDeviceLost } from '../utils/events.js'
import { EventEmitter } from '../utils/index.js'
import { ShaderModule, ShaderPairStateDescriptor } from '../resources/shader-module.js'
import { ShaderModule, ShaderPairDescriptor } from '../resources/shader-module.js'
import { RenderPipeline } from '../rendering/render-pipeline.js'
import { CommandRecorder } from './command-recorder.js'
import { BindGroupLayout } from '../resources/bind-group-layout.js'
@ -80,7 +80,7 @@ class GraphicsDeviceBuilder {
return this
}
async build() {
async finish() {
return new GraphicsDevice(
this._canvas,
new DeviceHandler(
@ -160,7 +160,7 @@ export class GraphicsDevice extends EventEmitter {
this._deviceHandler = deviceHandler
}
static build(canvas: HTMLCanvasElement) {
static build(canvas?: HTMLCanvasElement) {
return new GraphicsDeviceBuilder(canvas)
}
@ -195,7 +195,7 @@ export class GraphicsDevice extends EventEmitter {
return true
}
createBuffer(descriptor: GPUBufferDescriptor, data: ArrayBufferView | ArrayBuffer): Buffer {
createBuffer(descriptor: GPUBufferDescriptor, data?: ArrayBufferView | ArrayBuffer): Buffer {
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
try {
@ -247,7 +247,7 @@ export class GraphicsDevice extends EventEmitter {
}
}
createMaterial(shaders: ShaderPairStateDescriptor) {
createMaterial(shaders: ShaderPairDescriptor) {
if (!this._isInitialized) { throw GraphicsDeviceError.uninitialized() }
try {

View file

@ -1,6 +1,6 @@
export class RenderPipeline {
_handle
_label
_handle: GPURenderPipeline
_label: string
get handle() {
return this._handle
@ -10,11 +10,7 @@ export class RenderPipeline {
return this._label
}
/**
* @param {GPURenderPipeline} pipeline
* @param {string} [label]
*/
constructor(pipeline, label) {
constructor(pipeline: GPURenderPipeline, label: string) {
this._handle = pipeline
this._label = label
}

View file

@ -1,8 +1,8 @@
import { WebGPUObjectError } from '../utils/errors.js'
export class BindGroupLayout {
_device
_handle
_device: GPUDevice
_handle: GPUBindGroupLayout
get handle() {
return this._handle
@ -12,20 +12,12 @@ export class BindGroupLayout {
return this._handle.label
}
/**
* @param {GPUDevice} device
* @param {GPUBindGroupLayout} layout
*/
constructor(device, layout) {
constructor(device: GPUDevice, layout: GPUBindGroupLayout) {
this._device = device
this._handle = layout
}
/**
* @param {GPUDevice} device
* @param {GPUBindGroupLayoutDescriptor} descriptor
*/
static create(device, descriptor) {
static create(device: GPUDevice, descriptor: GPUBindGroupLayoutDescriptor) {
try {
return new BindGroupLayout(
device,

View file

@ -1,8 +1,8 @@
import { WebGPUObjectError } from '../utils/errors.js'
export class BindGroup {
_device
_handle
_device: GPUDevice
_handle: GPUBindGroup
get handle() {
return this._handle
@ -12,16 +12,12 @@ export class BindGroup {
* @param {GPUDevice} device
* @param {GPUBindGroup} bindGroup
*/
constructor(device, bindGroup) {
constructor(device: GPUDevice, bindGroup: GPUBindGroup) {
this._device = device
this._handle = bindGroup
}
/**
* @param {GPUDevice} device
* @param {GPUBindGroupDescriptor} descriptor
*/
static create(device, descriptor) {
static create(device: GPUDevice, descriptor: GPUBindGroupDescriptor) {
try {
return new BindGroup(
device,

View file

@ -1,29 +1,24 @@
import { Buffer } from './buffer.js'
/**
* @typedef GeometryDescriptor
* @param {Buffer} vertices
* @param {number} [vertexCount]
* @param {GPUVertexBufferLayout} layout
* @property {Buffer} [indices]
* @property {number} [indexCount]
* @property {GPUIndexFormat} [format]
*/
interface GeometryDescriptor {
vertices: Buffer
vertexCount: number
layout: GPUVertexBufferLayout
indices?: Buffer
indexCount?: number
format?: GPUIndexFormat
}
export class Geometry {
_device
_vertices
_vertexCount
_vertexBufferLayout
_indices
_indexCount
_format
_device: GPUDevice
_vertices: Buffer
_vertexCount: number
_vertexBufferLayout: GPUVertexBufferLayout
_indices: Buffer
_indexCount: number
_format: string
/**
* @param {GPUDevice} device
* @param {GeometryDescriptor} descriptor
*/
constructor(device, descriptor) {
constructor(device: GPUDevice, descriptor: GeometryDescriptor) {
this._device = device
this._vertices = descriptor.vertices
this._vertexCount = descriptor.vertexCount

View file

@ -1,29 +1,33 @@
import { BindGroupLayout } from './bind-group-layout.js'
import { ShaderPair, ShaderModule } from './shader-module.js'
import { FragmentStateDescriptor, ShaderPair, ShaderPairDescriptor, VertexStateDescriptor } from './shader-module.js'
import { MaterialError, WebGPUObjectError } from '../utils/errors.js'
import { ResourceType } from 'wgsl_reflect'
import { BindGroup } from './bind-group.js'
import { Texture } from './texture.js'
import { Buffer } from './buffer.js'
import { Sampler } from './sampler.js'
/** @import { FragmentStateDescriptor, VertexStateDescriptor } from './shader-module.js' */
/**
* @typedef ShaderPairDescriptor
* @property {ShaderModule} vertex
* @property {ShaderModule} [fragment]
*/
type BindingResource = Buffer | Texture | Sampler
/**
* @typedef {
ShaderPairDescriptor &
{ bindGroupLayouts?: BindGroupLayout[] }
* } MaterialDescriptor
*/
interface MaterialPipelineDescriptor {
label?: string
pipelineLayout?: GPUPipelineLayout
vertex: VertexStateDescriptor
fragment?: FragmentStateDescriptor
primitive?: GPUPrimitiveState
}
interface MaterialDescriptor extends ShaderPairDescriptor {
bindGroupLayouts?: BindGroupLayout[]
}
export class Material {
_device
_shaders
_bindGroupLayouts
_pipelineLayout
_device: GPUDevice
_shaders: ShaderPair
_bindGroupLayouts: BindGroupLayout[]
_pipelineLayout: GPUPipelineLayout
get shaders() {
return this._shaders
@ -33,11 +37,7 @@ export class Material {
return this._bindGroupLayouts
}
/**
* @param {GPUDevice} device
* @param {MaterialDescriptor} descriptor
*/
constructor(device, descriptor) {
constructor(device: GPUDevice, descriptor: MaterialDescriptor) {
this._device = device
this._shaders = Material._reflectShaders(descriptor)
const bgl = descriptor.bindGroupLayouts
@ -58,14 +58,8 @@ export class Material {
}
}
}
/**
* Attempts to handle shader modules which represent multiple
* shader types.
*
* @param {ShaderPairDescriptor} shaders
* @returns {ShaderPair}
*/
static _reflectShaders(shaders) {
static _reflectShaders(shaders: ShaderPairDescriptor): ShaderPair {
if (shaders == null) {
throw MaterialError.missingShader('vertex')
}
@ -75,20 +69,12 @@ export class Material {
}
}
/**
* @param {GPUDevice} device
* @param {ShaderPair} shaders
* @returns {BindGroupLayout[]}
*/
_reflectBindGroupLayouts(device, shaders) {
_reflectBindGroupLayouts(device: GPUDevice, shaders: ShaderPair): BindGroupLayout[] {
const layouts = shaders.createBindGroupLayoutEntries()
return layouts.map(entries => BindGroupLayout.create(device, { entries }))
}
/**
*
*/
createBindGroup(groupIndex, resources, label) {
createBindGroup(groupIndex: number, resources: Record<PropertyKey, BindingResource>, label?: string) {
if (groupIndex < 0 || groupIndex >= this._bindGroupLayouts.length) {
throw new Error(`Invalid bind group index: ${groupIndex}`)
}
@ -122,20 +108,7 @@ export class Material {
})
}
/**
* @typedef MaterialPipelineDescriptor
* @property {string} [label]
* @property {GPUPipelineLayout} [pipelineLayout]
* @property {VertexStateDescriptor} vertex
* @property {FragmentStateDescriptor} [fragment]
* @property {GPUPrimitiveState} [primitive]
*/
/**
* @param {MaterialPipelineDescriptor} descriptor
* @returns {GPURenderPipelineDescriptor}
*/
getRenderPipelineDescriptor(descriptor) {
getRenderPipelineDescriptor(descriptor: MaterialPipelineDescriptor): GPURenderPipelineDescriptor {
const { fragment, vertex } = this.shaders.getRenderPipelineStates(descriptor)
return {

View file

@ -4,8 +4,8 @@ import { ResourceType } from '../utils/internal-enums.js'
/** @import { BindGroupEntry } from '../core/graphics-device.js' */
export class Sampler {
_device
_handle
_device: GPUDevice
_handle: GPUSampler
get handle() {
return this._handle
@ -19,20 +19,12 @@ export class Sampler {
return ResourceType.Sampler
}
/**
* @param {GPUDevice} device
* @param {GPUSampler} sampler
*/
constructor(device, sampler) {
constructor(device: GPUDevice, sampler: GPUSampler) {
this._device = device
this._handle = sampler
}
/**
* @param {GPUDevice} device
* @param {GPUSamplerDescriptor} descriptor
*/
static create(device, descriptor) {
static create(device: GPUDevice, descriptor: GPUSamplerDescriptor) {
try {
return new Sampler(
device,

View file

@ -69,6 +69,11 @@ export interface ShaderPairStateDescriptor {
vertex: VertexStateDescriptor
}
export interface ShaderPairDescriptor {
fragment?: ShaderModule
vertex: ShaderModule
}
export class ReflectedShader {
static _reflectTypes = ['uniforms', 'storage', 'textures', 'samplers']
_module: ShaderModule
@ -292,10 +297,7 @@ export class ShaderPair {
)
}
static fromPair(value: {
vertex: ShaderModule
fragment?: ShaderModule
}) {
static fromPair(value: ShaderPairDescriptor) {
const vert = new ReflectedShader(value.vertex)
const frag = value.fragment && new ReflectedShader(value.fragment)
return new ShaderPair(vert, frag)

View file

@ -1,21 +1,24 @@
import { CommandRecorder } from '../core/command-recorder.js'
import { BitFlags } from '../utils/bitflags.js'
import { TextureDimension, TextureFormat } from '../enum.js'
import { WebGPUObjectError } from '../utils/errors.js'
import { ResourceType } from '../utils/internal-enums.js'
import { textureToImageDimension } from '../utils/wgsl-to-wgpu.js'
/** @import { BindGroupEntry } from '../core/graphics-device.js' */
interface UploadTextureInfo {
destination: GPUTexelCopyTextureInfo
dataLayout: GPUTexelCopyBufferLayout
size: GPUExtent3DStrict
}
export class Texture {
static _defaultUsage = GPUTextureUsage.TEXTURE_BINDING
| GPUTextureUsage.COPY_DST
| GPUTextureUsage.RENDER_ATTACHMENT
_device
_handle
_device: GPUDevice
_handle: GPUTexture
/** @type {GPUTextureView | undefined} */
_defaultView
_defaultView: GPUTextureView | undefined
get handle() {
return this._handle
@ -57,20 +60,12 @@ export class Texture {
return ResourceType.TextureView
}
/**
* @param {GPUDevice} device
* @param {GPUTexture} texture
*/
constructor(device, texture) {
constructor(device: GPUDevice, texture: GPUTexture) {
this._device = device
this._handle = texture
}
/**
* @param {GPUDevice} device
* @param {GPUTextureDescriptor} descriptor
*/
static create(device, descriptor) {
static create(device: GPUDevice, descriptor: GPUTextureDescriptor) {
try {
return new Texture(
device,
@ -81,18 +76,13 @@ export class Texture {
}
}
static _generateMipLevels(size) {
static _generateMipLevels(size: number[]) {
const max = Math.max.apply(undefined, size)
return 1 + Math.log2(max) | 0
}
/**
* @param {GPUDevice} device
* @param {string | URL} url
* @param {GPUTextureDescriptor} desciptor
*/
static async fromUrl(device, url, descriptor) {
static async fromUrl(device: GPUDevice, url: string | URL, descriptor: GPUTextureDescriptor) {
try {
const response = await fetch(url)
@ -100,8 +90,8 @@ export class Texture {
throw new Error(`Failed to fetch remote resource: ${response.statusText}`)
}
const usage = options.usage || Texture._defaultUsage
const dimension = descriptor.dimension ? textureToImageDimension(descriptor.dimension) : '2d'
const usage = descriptor.usage || Texture._defaultUsage
const dimension = descriptor.dimension ? textureToImageDimension(descriptor.dimension) : TextureDimension['2d']
const blob = await response.blob()
const bitmap = await createImageBitmap(blob)
@ -111,8 +101,8 @@ export class Texture {
usage,
dimension,
size,
format: descriptor.format || 'rgba8unorm',
mipLevelCount: descriptor.mipLevelCount || Texture._generateMipCount(...size),
format: descriptor.format || TextureFormat.Rgba8unorm,
mipLevelCount: descriptor.mipLevelCount || Texture._generateMipLevels(size),
...descriptor,
}
@ -126,13 +116,7 @@ export class Texture {
}
}
/**
* @param {GPUDevice} device
* @param {GPUExtent3DStrict} size
* @param {GPUTextureFormat} format
* @param {GPUTextureDescriptor} descriptor
*/
static createRenderTarget(device, size, format, descriptor) {
static createRenderTarget(device: GPUDevice, size: GPUExtent3DStrict, format: GPUTextureFormat, descriptor: GPUTextureDescriptor) {
const usage = descriptor.usage || Texture._defaultUsage
return Texture.create(device, {
@ -143,18 +127,8 @@ export class Texture {
})
}
/**
* @typedef UploadTextureInfo
* @property {GPUTexelCopyTextureInfo} destination
* @property {GPUTexelCopyBufferLayout} dataLayout
* @property {GPUExtent3DStrict} size
*/
/**
* @param {GPUAllowSharedBufferSource} source
* @param {UploadTextureInfo} [options={}]
*/
upload(source, options = {}) {
upload(source: GPUAllowSharedBufferSource, options?: UploadTextureInfo) {
const mipLevel = options.destination.mipLevel || 0
const size = options.size || [
Math.max(1, this.width >> mipLevel),
@ -174,28 +148,11 @@ export class Texture {
}
}
/**
* @param {GPUCommandEncoder} commandEncoder
*/
generateMipmaps(commandEncoder) {
const requiredUsage = GPUTextureUsage.COPY_SRC
| GPUTextureUsage.COPY_DST
| GPUTextureUsage.RENDER_ATTACHMENT
if (!BitFlags.has(this.usage & requiredUsage)) {
throw new Error('Texture does not have the required usage flags for mipmap generation')
}
for (let i = 1; i < this.mipLevelCount; ++i) {
}
generateMipmaps(_commandEncoder: GPUCommandEncoder) {
// TODO: use MipGenerator
}
/**
* @param {GPUTextureViewDescriptor} [descriptor]
* @throws {TextureError}
*/
createDefaultView(descriptor) {
createDefaultView(descriptor?: GPUTextureViewDescriptor) {
if (!descriptor && this._defaultView) {
return this._defaultView
}

View file

@ -1,73 +1,49 @@
/**
* @typedef {'read' | 'write' | 'read_write'} WGSLAccess
* @typedef {'f32' | 'i32' | 'u32'} WGSLSampledType
*/
import { BufferBindingType, StorageTextureAccess, TextureFormat } from '../enum.js'
/**
* @typedef {
'texture_1d'
| 'texture_2d'
| 'texture_2d_array'
| 'texture_3d'
| 'texture_cube'
| 'texture_cube_array'
* } WGSLSampledTextureType
*
* @typedef {
'texture_multisampled_2d'
| 'texture_depth_multisampled_2d'
* } WGSLMultisampledTextureType
*
* @typedef {
'texture_storage_1d'
| 'texture_storage_2d'
| 'texture_storage_2d_array'
| 'texture_storage_3d'
* } WGSLStorageTextureType
*
* @typedef {
'texture_depth_2d'
| 'texture_depth_2d_array'
| 'texture_depth_cube'
| 'texture_depth_cube_array'
* } WGSLDepthTextureType
*
* @typedef {
WGSLSampledTextureType
| WGSLMultisampledTextureType
| WGSLStorageTextureType
| WGSLDepthTextureType
* } WGSLTextureType
*/
export type WGSLAccess = 'read' | 'write' | 'read_write'
export type WGSLSampledType = 'f32' | 'i32' | 'u32'
export type WGSLSampledTextureType = 'texture_1d'
| 'texture_2d'
| 'texture_2d_array'
| 'texture_3d'
| 'texture_cube'
| 'texture_cube_array'
/**
* @typedef {
'sampler'
| 'sampler_comparison'
* } WGSLSamplerType
*/
export type WGSLMultisampledTextureType = 'texture_multisampled_2d'
| 'texture_depth_multisampled_2d'
/**
* @param {string} typeName
* @returns {[WGSLTextureType, WGSLSampledType]}
*/
export const parseTextureType = (typeName) => {
export type WGSLStorageTextureType = 'texture_storage_1d'
| 'texture_storage_2d'
| 'texture_storage_2d_array'
| 'texture_storage_3d'
export type WGSLDepthTextureType = 'texture_depth_2d'
| 'texture_depth_2d_array'
| 'texture_depth_cube'
| 'texture_depth_cube_array'
export type WGSLTextureType = WGSLSampledTextureType
| WGSLMultisampledTextureType
| WGSLStorageTextureType
| WGSLDepthTextureType
export type WGSLSamplerType = 'sampler'
| 'sampler_comparison'
export const parseTextureType = (typeName: string): [WGSLTextureType, WGSLSampledType] => {
const chevronIndex = typeName.indexOf('<')
const type = typeName.slice(0, chevronIndex)
const sampledType = typeName.slice(chevronIndex + 1, -1)
// FIXME: there's no validation
return [
/** @type {WGSLTextureType} */ (type),
/** @type {WGSLSampledType} */ (sampledType)
type as WGSLTextureType,
sampledType as WGSLSampledType
]
}
/**
* @param {WGSLAccess} access
* @returns {GPUBufferBindingType}
*/
export const accessToBufferType = access => {
export const accessToBufferType = (access: WGSLAccess): GPUBufferBindingType => {
switch (access) {
case 'read': return BufferBindingType.ReadOnlyStorage
case 'write':
@ -77,11 +53,7 @@ export const accessToBufferType = access => {
}
}
/**
* @param {WGSLAccess} access
* @returns {GPUStorageTextureAccess}
*/
export const accessToStorageTextureAccess = access => {
export const accessToStorageTextureAccess = (access: WGSLAccess): GPUStorageTextureAccess => {
switch (access) {
case 'read': return StorageTextureAccess.ReadOnly
case 'write': return StorageTextureAccess.WriteOnly
@ -135,8 +107,7 @@ export const FormatToFilterType = Object.freeze({
stencil8: 'uint'
})
/** @param {string} format */
export const formatToFilterType = format => (
export const formatToFilterType = (format: string) => (
FormatToFilterType[format] || 'float'
)
@ -186,14 +157,12 @@ export const FormatToStride = Object.freeze({
stencil8: 1
})
/** @param {string} typeName */
export const typeToStride = typeName => (
export const typeToStride = (typeName: string) => (
FormatToStride[typeName] || 1
)
/** @param {WGSLTextureType} typeName */
export const typeToViewDimension = typeName => {
export const typeToViewDimension = (typeName: WGSLTextureType) => {
switch (typeName) {
case 'texture_1d':
case 'texture_storage_1d':
@ -228,11 +197,7 @@ export const typeToViewDimension = typeName => {
}
}
/**
* @param {GPUTextureViewDimension} dimension
* @returns {GPUTextureDimension}
*/
export const textureToImageDimension = dimension => {
export const textureToImageDimension = (dimension: GPUTextureViewDimension): GPUTextureDimension => {
switch (dimension) {
case '1d':
return '1d'
@ -250,11 +215,7 @@ export const textureToImageDimension = dimension => {
}
}
/**
* @param {string} format
* @returns {GPUTextureFormat}
*/
export const wgslToWgpuFormat = (format) => {
export const wgslToWgpuFormat = (format: string): GPUTextureFormat => {
switch (format) {
case 'f32':
return TextureFormat.Bgra8unorm
@ -299,12 +260,7 @@ export const wgslToWgpuFormat = (format) => {
}
}
/**
* @param {WGSLTextureType} type
* @param {WGSLSampledType} sampledType
* @returns {GPUTextureSampleType}
*/
export const typeToTextureSampleType = (type, sampledType) => {
export const typeToTextureSampleType = (type: WGSLTextureType, sampledType: WGSLSampledType): GPUTextureSampleType => {
if (type.includes('depth')) {
return 'depth'
}
@ -318,11 +274,7 @@ export const typeToTextureSampleType = (type, sampledType) => {
}
}
/**
* @param {WGSLSamplerType} type
* @returns {GPUSamplerBindingType}
*/
export const typeToSamplerBindingType = type => {
export const typeToSamplerBindingType = (type: WGSLSamplerType): GPUSamplerBindingType => {
switch (type) {
case 'sampler_comparison':
return 'comparison'