storageBuffer
On this page

storageBuffer()

Function in Layout

Declare a bound array<Element> storage buffer from its element alone.

import { storageBuffer } from 'typeshade'

The signature, the description and the examples come from the compiler's own source at commit 26de7be8.

Syntax

function storageBuffer<H extends StructHandle>(
name: string,
element: H,
at: { group: number; binding: number; access: 'read' },
): StorageBuffer<ReturnType<H['of']>>;
function storageBuffer<H extends StructHandle>(
name: string,
element: H,
at: { group: number; binding: number; access: 'read_write' },
): StorageBuffer<MutableView<ReturnType<H['of']>>>;
function storageBuffer<T extends ShaderType>(
name: string,
element: T,
at: { group: number; binding: number; access: 'read' },
): StorageBuffer<ReadonlyNode<KeyOf<T>>>;
function storageBuffer<T extends ShaderType>(
name: string,
element: T,
at: { group: number; binding: number; access: 'read_write' },
): StorageBuffer<Node<KeyOf<T>>>;

Parameters

name string

the emitted binding name, which is also the name a host binds by.

element H extends StructHandle

the array’s element: a struct handle, or a scalar or vector type.

at { group: number; binding: number; access: 'read' }

the group and binding slot, and access, 'read' or 'read_write'.

Return value

StorageBuffer<ReturnType<H['of']>>

the StorageBuffer handle described above.

Exceptions

UnsupportedFeatureError

This path only reads: a read_write binding throws UnsupportedFeatureError at GLSL emit, so a write is never silently dropped.

Description

The element is a struct handle (structDecl or ioStruct) or a scalar or vector shader type. The handle derives the binding declaration, the access node, and a typed .at(i). The array is runtime-length, sized by whatever buffer the host binds, so it takes no count.

.at(i) returns the element’s typed field proxy for a struct element, so buf.at(i).p0 is a typed read with no .of() and no element-type argument. For a scalar element it returns the element node directly. .binding goes in module({ bindings }), or pass the handle in uses, which registers the element struct too.

access decides the element view’s write capability at the type level: 'read' hands out read-only fields, so buf.at(i).p0.assign(...) is a tsc error, and 'read_write' hands out mutable ones.

WebGL2 has no storage buffers, so on GLSL ES 3.00 the array is emitted as a data texture and each read becomes a texelFetch. Nothing changes at the authoring site. This path only reads: a read_write binding throws UnsupportedFeatureError at GLSL emit, so a write is never silently dropped. A compute kernel’s output takes the compute-to-fragment path; declare the kernel portable: true.

The host has one obligation the DSL cannot check for it: give that data texture the internal format matching the element. array<u32> becomes a usampler2D and wants R32UI, array<i32> an isampler2D and wants R32I, and the float case wants R32F. A texture whose format disagrees with its sampler type is merely incomplete, which raises nothing: texelFetch on it returns zero. Read the element off reflect(m), whose per-binding textureElem reports it.

Carrying integers through an R32F texture and recovering them with floatBitsToUint is unsafe even though it usually works. GLSL ES 3.00 permits an implementation to flush any denormal to zero, and small integers are denormal f32 bit patterns (1u is 1.4e-45), so that route can legally lose values.

Examples

Example

import { storageBuffer, structDecl, u32T, vec2fT } from 'typeshade'
const ShapeSegment = structDecl('ShapeSegment', { kind: u32T, p0: vec2fT, p1: vec2fT })
const segments = storageBuffer('segments', ShapeSegment, { group: 0, binding: 9, access: 'read' })
const seg = segments.at(i)
seg.p0 // Node<'vec2<f32>'>
const featIds = storageBuffer('feat_ids', u32T, { group: 0, binding: 0, access: 'read' })
featIds.at(i) // Node<'u32'>: an SSBO read on WGSL, a texelFetch on GLSL

In the guide

See also

Source

src/core/sot.ts, line 1092, at commit 26de7be8

Edit this page Report a problem