Reflection
On this page

Reflection

Interface in Reflection

The target-neutral pipeline metadata reflect recovers from a module: bind-group layout, every bound struct's byte layout, the vertex-attribute layout if any (vertex), every entry point's signature (entries), specialization constants (overrides), the capabilities a host must activate before pipeline creation, and the host-provided globals the module expects (requires).

import type { Reflection } from 'typeshade'

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

Syntax

interface Reflection {
readonly bindGroups: readonly BindGroup[];
readonly uniforms: readonly StructLayout[];
readonly storage: readonly StructLayout[];
readonly vertex?: VertexLayout;
readonly entries: readonly EntryInfo[];
readonly overrides: readonly OverrideInfo[];
readonly requiredFeatures: readonly Capability[];
readonly requiredLanguageFeatures: readonly LanguageFeature[];
readonly requires: readonly ExternRequirement[];
}

Description

Each field’s own doc gives its shape. A host that takes its bind-group layouts and byte offsets from a reflect() call on the same module it emits shader text from keeps one table, so a struct field cannot sit at byte 20 in the shader and at byte 24 in the CPU packer.

Instance properties

bindGroupsread only readonly BindGroup[]
uniformsread only readonly StructLayout[]

std140 uniform-buffer struct layouts (one per uniform binding whose type is a struct).

storageread only readonly StructLayout[]

std430 storage-buffer struct layouts.

vertexoptionalread only VertexLayout

Vertex attributes from the first @vertex entry’s @location inputs, its loose parameters and the located fields of its struct parameters, tightly packed in the order written: the layout packModule() and the manifest carry (Rule 6.8).

entriesread only readonly EntryInfo[]
overridesread only readonly OverrideInfo[]

Pipeline specialization constants: the names, types and defaults the host passes at pipeline creation (WGSL constants, or the GLSL #define header). Always present; empty for a module that declares no overrides.

requiredFeaturesread only readonly Capability[]

Every capability this module’s emit requires: the ones derived from the module’s shape (a storage binding needs storageBuffer, a @compute entry needs compute, a multisampled texture load needs msaaTextureLoad) plus everything the module declared in enables. Sorted and deduplicated. Always present; empty for a module that needs nothing.

This is what a host must have active before it creates a pipeline for the module. The ids are neutral, because reflection takes a module and never a backend, so translate them for one target with hostFeaturesFor:

for (const ext of hostFeaturesFor(glslEs300Backend, reflect(m).requiredFeatures)) {
if (!gl.getExtension(ext)) throw new Error(`WebGL2 lacks ${ext}`)
}
// WebGPU fixes its features at requestDevice, so do this at boot, before any pipeline:
// requestDevice({
// requiredFeatures: hostFeaturesFor(wgslBackend, reflect(m).requiredFeatures) })

A capability the target backend cannot provide at all normally never reaches this loop in a usable pipeline, because emit for that backend already throws UnsupportedFeatureError (SD0030) naming it. The exception is storageBuffer on GLSL ES 3.00: a storage binding is emitted as a data texture there, so the module emits fine while reflection of the module as authored still reports the capability. hostFeaturesFor skips every capability the backend has no host feature for, so the loop above is correct either way.

requiredLanguageFeaturesread only readonly LanguageFeature[]

Every WGSL language extension this module needs (§50), sorted and deduplicated. Always present; empty for a module that needs none, which is most.

This is the requires axis of WGSL, not the enable axis requiredFeatures reports: a language extension changes what the WGSL text may say, and it is not requested at requestDevice — it is either present in the browser’s WGSL implementation or not, so a host checks it against navigator.gpu.wgslLanguageFeatures before it builds the module. GLSL ES 3.00 has no such axis, and the resource capability carrying each row is what fails a module closed on that target.

Two rows today, and the writer directs only one of them — reporting a feature and writing requires for it are separate decisions, each measured. A storage texture bound read or read_write (or a textureBarrier call) needs readonly_and_readwrite_storage_textures to be a program at all, since core WGSL gives a storage texture write only, and the directive is accepted by the Tint the gate runs, so it is emitted. The packed 4x8 integer family reports packed_4x8_integer_dot_product and emits nothing: all eight builtins compile bare on the same Tint and the directive “changes nothing”, so writing it could only fail a module closed on a browser that lacks the name.

for (const f of reflect(m).requiredLanguageFeatures) {
if (!navigator.gpu.wgslLanguageFeatures.has(f)) throw new Error(`no WGSL ${f}`)
}
requiresread only readonly ExternRequirement[]

The host-provided globals this module references but does not declare: one entry per externVar declarator, reported so a composer can check them against what the host’s prelude actually supplies. Always present; empty for a module that expects nothing from its host. Host-provided functions (externFn) have no declaration to report here; the requires list of an emitted fragment (emitFragment) covers those by walking call sites.

See also

Source

src/core/reflect.ts, line 597, at commit 26de7be8

Edit this page Report a problem