Backend
이 페이지에서

Backend

인터페이스, 백엔드 분류

The contract a target writer implements.

import type { Backend } from 'typeshade'

시그니처, 설명, 예제는 커밋 c66579bf의 컴파일러 소스에서 그대로 가져온 영어 원문입니다.

구문

interface Backend {
readonly id: string;
readonly capProfile: CapProfile;
readonly absentBuiltins?: ReadonlyMap<string, string>;
typeName(t: ShaderType): string;
literal(value: number | boolean, t: ShaderType): string;
intrinsic(name: string, args: string[]): string;
localLet(name: string, type: ShaderType, init: string): string;
localVar(name: string, type: ShaderType, init?: string): string;
constDecl(name: string, type: ShaderType, value: string): string;
caseLabel(value: number, scrutType: ShaderType): string;
caseLabels?(labels: readonly string[]): string;
switchHead(scrut: string): string;
readonly floatMod?: (a: string, b: string) => string;
readonly intBinop?: ( bop: BinOp, b: Expr, aText: string, bText: string, type: ShaderType, ) => string | undefined;
readonly floatToInt?: (to: ShaderType, from: ShaderType, argText: string) => string | undefined;
readonly vectorCompare?: (cop: CmpOp, a: string, b: string) => string;
readonly vectorSelect?: ( ifFalse: string, ifTrue: string, cond: string, type: ShaderType, ) => string;
readonly caseBreak?: string;
rawStmt(s: RawStmt): string;
placeholderStmt(tag: string): string;
readonly phonyAssign?: string;
emitConst(c: ConstDecl): string;
emitOverride?(o: OverrideDecl): string;
emitStruct(s: StructDecl): string;
emitBinding(b: BindingDecl): string;
emitModuleVar?(v: ModuleVarDecl): string;
emitFunc(f: FuncDecl, parens?: ParenMode): string;
paramDecl?(p: FuncDecl['params'][number]): string;
reference?(lvalue: string): string;
dereference?(name: string): string;
optimize(lowered: ModuleDecl): ModuleDecl;
preOptimize?(lowered: ModuleDecl): ModuleDecl;
postLower?(lowered: ModuleDecl): ModuleDecl;
modulePreamble?(m: ModuleDecl): string;
}

설명

The package ships two backends, WGSL and GLSL ES 3.00, and the shared emit driver never branches on which one it is writing for: every target-specific decision (how types, literals and intrinsic calls are spelled, the handful of statement fragments that differ between targets, and the module-level declarations) is a method call into whichever Backend the caller passed.

capProfile is the one member that spells no code. The capability gate reads it before any of the methods run, so a module that needs a capability the target lacks throws UnsupportedFeatureError, naming the missing capabilities, before the backend can emit source the driver would reject. The GLSL ES 3.00 profile has no row for storageBuffer, compute or msaaTextureLoad, because WebGL2 has neither compute shaders nor multisampled texture loads, while the WGSL profile gives the same three an empty row: core, nothing to declare or activate. A capability can also be core on one target and an extension on the other: floatRenderTarget is {} on WGSL and needs EXT_color_buffer_float activated on GLSL. A backend whose target cannot represent a declaration (GLSL has no syntax for a struct or binding at a bare emit site) throws the same error from that method.

인스턴스 속성

id읽기 전용 string
capProfile읽기 전용 CapProfile

The capability table for this target: neutral capability id to { directive?, hostFeature? }, and the only capability authority a backend carries. Coverage is Capabilities.fromProfile(capProfile), modulePreamble reads the directive fields off the same rows, and hostFeaturesFor reads the hostFeature fields, so a capability cannot be supported without its directive, or directed without being supported. A target gains support for a feature by gaining one row.

absentBuiltins선택 사항읽기 전용 ReadonlyMap<string, string>

Optional. The @builtin(<id>) ids this target lacks, each mapped to the message tail printed after <backend id>: @builtin(<id>). A pre-pass that runs beside the capability gate throws UnsupportedFeatureError for any of them, so the problem is named at the author’s module with the whole module as context. A backend whose own input/output translation already rejects every builtin it cannot map (GLSL ES 3.00 does) omits this.

floatMod선택 사항읽기 전용 (a: string, b: string) => string

Optional. Spelling for % on float operands, for a target whose native % accepts integers only. GLSL ES 3.00 rejects float %, so the GLSL backend provides this. The operand texts arrive fully parenthesized (single atoms aside), and the returned expression must be wrapped in its own parentheses and must reproduce WGSL float % semantics: truncated modulo, a - b * trunc(a / b). GLSL mod() is floor modulo and gives a different answer for negative operands, so it is the wrong choice here. When absent, the native % is emitted.

intBinop선택 사항읽기 전용 ( bop: BinOp, b: Expr, aText: string, bText: string, type: ShaderType, ) => string | undefined

Optional. Spelling for an integer /, % or shift on a target whose bare operator gives some input WGSL settles no result (GLSL ES 3.00, Rule 11.12): a zero divisor, the least i32 over -1, a negative operand of %, a shift amount of 32 or more. b is the right operand as written, so a literal divisor or amount can keep the bare operator; aText and bText arrive as primaries, and the result is one too. undefined keeps the bare operator, and so does a target that omits this.

floatToInt선택 사항읽기 전용 (to: ShaderType, from: ShaderType, argText: string) => string | undefined

Optional. Spelling for a float’s conversion to the integer type to, a scalar or a vector, on a target whose bare conversion gives an out-of-range or NaN source no result (GLSL ES 3.00, Rule 11.12); WGSL saturates, and leaves a NaN source indeterminate, which the oracle takes as 0. undefined keeps the bare conversion.

vectorCompare선택 사항읽기 전용 (cop: CmpOp, a: string, b: string) => string

Optional. Spelling for a comparison of two vectors, which yields a vector of bools (roadmap 0.2 item 7). WGSL has the operator form and omits this; GLSL ES 3.00 has only the functions lessThan, equal and their siblings, so the GLSL backend provides it.

vectorSelect선택 사항읽기 전용 ( ifFalse: string, ifTrue: string, cond: string, type: ShaderType, ) => string

Optional. Spelling for select(f, t, c) when c is a vector of bools and the pick is per component. WGSL’s select takes it and omits this; GLSL ES 3.00’s ternary does not, so the GLSL backend spells mix(f, t, c) for a float vector and a componentwise ternary otherwise. type is the type of the result.

caseBreak선택 사항읽기 전용 string

Optional. A terminator written at the end of every switch case. WGSL cases do not fall through, so the WGSL backend omits this. GLSL follows C and does fall through, so the GLSL backend returns break;; without it every match() arm would run into the next and the function would return the last arm’s value.

phonyAssign선택 사항읽기 전용 string

Optional. The prefix a value-dropping call statement takes when the callee is a value-returning builtin. WGSL treats every such builtin as @must_use, so a bare max(a, b); is rejected and the phony assignment _ = max(a, b); is required, while a user function’s dropped result is accepted bare. GLSL ES 3.00 takes the bare call in every case, so the GLSL backend omits this.

인스턴스 메서드

typeName (t: ShaderType): string

Spell a type for this target (e.g. WGSL vec3<f32> vs GLSL vec3).

literal (value: number | boolean, t: ShaderType): string

Spell a scalar literal for this target (e.g. WGSL 1u vs GLSL 1).

intrinsic (name: string, args: string[]): string

Spell an intrinsic or builtin call from already-emitted argument strings. name is the WGSL id of the function (the call node’s fn, plus the reserved 'select'). The WGSL writer emits name(args) as is; the GLSL writer remaps the names that differ (textureSample to texture, unpack4x8unorm to unpackUnorm4x8, bitcast to floatBitsToUint, select(f, t, c) to a ternary) and passes the rest through. Calls to user-defined functions also arrive here and pass through unchanged.

localLet (name: string, type: ShaderType, init: string): string

let n = init (WGSL, type inferred) vs T n = init (GLSL).

localVar (name: string, type: ShaderType, init?: string): string

var n: T[= init] (WGSL) vs T n[= init] (GLSL).

constDecl (name: string, type: ShaderType, value: string): string

A module-level const declaration line, incl. trailing ;: const n: T = v; (WGSL) vs const T n = v; (GLSL).

caseLabel (value: number, scrutType: ShaderType): string

A switch case label: ${v}u for a u32 scrutinee on WGSL; ${v} on GLSL.

caseLabels (labels: readonly string[]): string

The whole case …: prefix for a clause, given each selector already spelled by Backend.caseLabel. Only a target whose multi-selector form is not WGSL’s case a, b: declares one: GLSL ES 3.00 stacks case a: case b: instead. Absent, the emitter writes case ${'${labels.join(\', \')}'}:, which is also the one-selector spelling every backend wrote before a clause could hold more than one.

switchHead (scrut: string): string

The switch head: switch ${scrut} { (WGSL) vs switch (${scrut}) { (GLSL).

rawStmt (s: RawStmt): string

A raw statement, the escape hatch that splices source text verbatim. It takes the whole node because the node carries one payload per target (wgsl and glsl); each backend picks its own side here, and the shared emit walk stays target-blind. A backend whose side is absent throws UnsupportedFeatureError (SD0030).

placeholderStmt (tag: string): string

A placeholder statement that no composition step replaced before emit. The WGSL backend emits a comment carrying the tag; the GLSL backend throws UnsupportedFeatureError.

emitConst (c: ConstDecl): string

A module-level const declaration line, incl. trailing ;.

emitOverride (o: OverrideDecl): string

Optional. A module-level specialization constant declaration. The WGSL writer emits override name: T = default;, which the host can override at pipeline creation through constants: {}. The GLSL writer emits an #ifndef / #define / #endif block that supplies the default; the host specializes by emitting again with emitGlslModule’s overrideValues, which the emitter places after the #version line, since a #define ahead of #version is invalid GLSL. A backend with no such construct omits this, and module assembly skips overrides for that target.

emitStruct (s: StructDecl): string

A struct declaration block.

emitBinding (b: BindingDecl): string

A resource binding declaration line.

emitModuleVar (v: ModuleVarDecl): string

Optional. A module-scope variable that is not a resource (ModuleVarDecl). The WGSL writer spells var<workgroup> x: T; and var<private> y: T = init;; the GLSL writer spells a private variable as a plain global, which GLSL ES 3.00 gives every invocation its own copy of, and fails closed on a workgroup one, since WebGL2 has no workgroup memory. A GLSL global with no initializer is undefined, so that writer spells the zero of one the IR gives none; the zero of a struct lists its fields, which emitGlslModule has and this method alone does not, so here it fails closed. A backend that omits this cannot emit a module that declares one.

emitFunc (f: FuncDecl, parens?: ParenMode): string

A function declaration block: the signature and the emitted body. parens selects how many parentheses the shared expression walk writes, 'full' or 'minimal'; omitted means 'full'. A backend forwards it to the body emitter.

paramDecl (p: FuncDecl['params'][number]): string

Optional. How this target spells a parameter the callee writes through (FuncDecl.params[i].mode === 'inout'): GLSL ES 3.00 inout vec3 v, WGSL v: ptr<function, vec3<f32>>. A backend that omits it takes such a parameter by value, which is what every backend did before the mode existed.

reference (lvalue: string): string

Optional, and required alongside paramDecl when the spelling is a pointer rather than a qualifier. reference is the argument a call passes for an inout parameter (WGSL &x), and dereference is how the callee’s body reads the parameter itself (WGSL (*x)). GLSL needs neither: its inout argument and its uses are written plainly.

dereference (name: string): string
optimize (lowered: ModuleDecl): ModuleDecl

The backend’s emit-time optimization of the lowered module. Both shipped backends run the same optimization pipeline; the hook is per backend so that a target can choose differently without a change to the shared driver. It runs after the lowering passes and before the module is assembled into source.

preOptimize (lowered: ModuleDecl): ModuleDecl

Optional. The target’s own lowerings, run after the shared lowering passes and before either optimizer tier — the explicit-level one as well as optimize.

The distinction is what a pass is, not when it happens to be convenient. An optimizer makes a module faster and may be skipped; a lowering makes it the module the target accepts and may not. WGSL’s uniform 16-byte array padding (§51) and its @interpolate(flat) derivation (§53) are lowerings, and while they lived inside optimize the public emitModuleAt(m, level) and lowerWgsl(m, level) skipped both and wrote the exact two programs the default path was fixed to stop writing — a bare @location(0) id: u32 Tint refuses, and an unpadded uniform array.

Before the optimizer, not after it in postLower: the padding changes a struct’s member types and the reads that reach through them, and an optimizer that has not seen it hoists the unlowered form. LICM lifted U.weights out of a loop as let _licm0 = U.weights; and the padding then had no member node left to rewrite.

postLower (lowered: ModuleDecl): ModuleDecl

Optional. The last rewrite before the module is spelled, run after optimize and after every optimizer tier, for a target whose spelling needs a shape the IR does not carry. WGSL uses it to give a function with a pointer parameter one copy per address space its calls use, which is a fact about WGSL’s pointer types and about nothing else.

modulePreamble (m: ModuleDecl): string

Optional. The module header carrying the source-level directives the module’s declared capabilities need on this target: one line per m.enables entry whose capProfile row has a directive (WGSL enable <d>;, GLSL ES 3.00 #extension <d> : require), deduplicated and sorted so the byte order is deterministic.

The contract is the same for every backend: the directive lines joined by '\n', with no trailing separator, and '' when there is nothing to direct (every declared capability is host-side, or none is declared). The caller separates the header from what follows and decides where it lands, because the two targets have different legal slots: the WGSL driver prepends it to the whole module with a blank line after it, and the GLSL assembler splices it in after #version 300 es, since GLSL ES 3.00 requires #version to lead the file and #extension to precede any non-preprocessor token. Both call it with the module as authored: enables is an authoring-level declaration, and reading it off the lowered module would make the header depend on every pass preserving it.

함께 보기

소스

src/core/backend.ts, 170행, 커밋 c66579bf 기준

이 페이지 편집 문제 보고