Skip to content

Commit 43d13ad

Browse files
feat: GLSL ES 3.1 support (#29)
1 parent 4bdd969 commit 43d13ad

File tree

9 files changed

+506
-98
lines changed

9 files changed

+506
-98
lines changed

README.md

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ Tools and IntelliSense for GLSL and WGSL.
2525
- [BlockStatement](#blockstatement)
2626
- [DiscardStatement](#discardstatement)
2727
- [PreprocessorStatement](#preprocessorstatement)
28-
- [PrecisionStatement](#precisionstatement)
29-
- [InvariantStatement](#invariantstatement)
28+
- [PrecisionQualifierStatement](#precisionqualifierstatement)
29+
- [InvariantQualifierStatement](#invariantqualifierstatement)
30+
- [LayoutQualifierStatement](#layoutqualifierstatement)
3031
- Control Flow
3132
- [ReturnStatement](#returnstatement)
3233
- [BreakStatement](#breakstatement)
@@ -44,7 +45,7 @@ Tools and IntelliSense for GLSL and WGSL.
4445
- [FunctionParameter](#functionparameter)
4546
- [VariableDeclaration](#variabledeclaration)
4647
- [VariableDeclarator](#variabledeclarator)
47-
- [UniformDeclarationBlock](#uniformdeclarationblock)
48+
- [StructuredBufferDeclaration](#structuredbufferdeclaration)
4849
- [StructDeclaration](#structdeclaration)
4950
- Expressions
5051
- [ArrayExpression](#arrayexpression)
@@ -395,29 +396,41 @@ interface PreprocessorStatement extends Node {
395396
}
396397
```
397398

398-
### PrecisionStatement
399+
### PrecisionQualifierStatement
399400

400-
A GLSL precision statement.
401+
A GLSL precision qualifier statement.
401402

402403
```ts
403-
interface PrecisionStatement extends Node {
404-
type: 'PrecisionStatement'
404+
interface PrecisionQualifierStatement extends Node {
405+
type: 'PrecisionQualifierStatement'
405406
precision: PrecisionQualifier
406407
typeSpecifier: Identifier
407408
}
408409
```
409410

410-
### InvariantStatement
411+
### InvariantQualifierStatement
411412

412-
A GLSL invariant statement.
413+
A GLSL invariant qualifier statement.
413414

414415
```ts
415-
interface InvariantStatement extends Node {
416-
type: 'InvariantStatement'
416+
interface InvariantQualifierStatement extends Node {
417+
type: 'InvariantQualifierStatement'
417418
typeSpecifier: Identifier
418419
}
419420
```
420421

422+
### LayoutQualifierStatement
423+
424+
A layout qualifier statement.
425+
426+
```ts
427+
interface LayoutQualifierStatement extends Node {
428+
type: 'LayoutQualifierStatement'
429+
layout: Record<string, string | boolean>
430+
qualifier: StorageQualifier
431+
}
432+
```
433+
421434
### ReturnStatement
422435

423436
A return statement with an optional argument.
@@ -546,7 +559,7 @@ A function parameter within a function declaration.
546559
```ts
547560
interface FunctionParameter extends Node {
548561
type: 'FunctionParameter'
549-
id: Identifier
562+
id: Identifier | null
550563
qualifiers: (ConstantQualifier | ParameterQualifier | PrecisionQualifier)[]
551564
typeSpecifier: Identifier | ArraySpecifier
552565
}
@@ -578,15 +591,15 @@ interface VariableDeclarator extends Node {
578591
}
579592
```
580593

581-
### UniformDeclarationBlock
594+
### StructuredBufferDeclaration
582595

583-
A uniform declaration block with optional layout and qualifiers.
596+
A buffer interface declaration with optional layout and qualifiers.
584597

585598
```ts
586-
interface UniformDeclarationBlock extends Node {
587-
type: 'UniformDeclarationBlock'
599+
interface StructuredBufferDeclaration extends Node {
600+
type: 'StructuredBufferDeclaration'
588601
id: Identifier | null
589-
qualifiers: LayoutQualifier[]
602+
qualifiers: (InterfaceStorageQualifier | MemoryQualifier | LayoutQualifier)[]
590603
typeSpecifier: Identifier | ArraySpecifier
591604
layout: Record<string, string | boolean> | null
592605
members: VariableDeclaration[]

src/ast.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ export interface ForStatement extends Node {
275275
export type ConstantQualifier = 'const'
276276
export type ParameterQualifier = 'in' | 'out' | 'inout'
277277
export type StorageQualifier = 'uniform' | 'in' | 'out'
278+
export type InterfaceStorageQualifier = 'uniform' | 'buffer'
279+
export type MemoryQualifier = 'coherent' | 'volatile' | 'restrict' | 'readonly' | 'writeonly'
278280

279281
export type InterpolationQualifier = 'centroid' | 'smooth' | 'flat' | 'invariant'
280282
export type LayoutQualifier = 'location' | 'std140' | 'packed' | 'shared'
@@ -297,7 +299,7 @@ export interface FunctionDeclaration extends Node {
297299
*/
298300
export interface FunctionParameter extends Node {
299301
type: 'FunctionParameter'
300-
id: Identifier
302+
id: Identifier | null
301303
qualifiers: (ConstantQualifier | ParameterQualifier | PrecisionQualifier)[]
302304
typeSpecifier: Identifier | ArraySpecifier
303305
}
@@ -325,10 +327,10 @@ export interface VariableDeclarator extends Node {
325327
/**
326328
* A uniform declaration block with optional layout and qualifiers.
327329
*/
328-
export interface UniformDeclarationBlock extends Node {
329-
type: 'UniformDeclarationBlock'
330+
export interface StructuredBufferDeclaration extends Node {
331+
type: 'StructuredBufferDeclaration'
330332
id: Identifier | null
331-
qualifiers: LayoutQualifier[]
333+
qualifiers: (InterfaceStorageQualifier | MemoryQualifier | LayoutQualifier)[]
332334
typeSpecifier: Identifier | ArraySpecifier
333335
layout: Record<string, string | boolean> | null
334336
members: VariableDeclaration[]
@@ -353,22 +355,31 @@ export interface PreprocessorStatement extends Node {
353355
}
354356

355357
/**
356-
* A GLSL precision statement.
358+
* A GLSL precision qualifier statement.
357359
*/
358-
export interface PrecisionStatement extends Node {
359-
type: 'PrecisionStatement'
360+
export interface PrecisionQualifierStatement extends Node {
361+
type: 'PrecisionQualifierStatement'
360362
precision: PrecisionQualifier
361363
typeSpecifier: Identifier
362364
}
363365

364366
/**
365-
* A GLSL invariant statement.
367+
* A GLSL invariant qualifier statement.
366368
*/
367-
export interface InvariantStatement extends Node {
368-
type: 'InvariantStatement'
369+
export interface InvariantQualifierStatement extends Node {
370+
type: 'InvariantQualifierStatement'
369371
typeSpecifier: Identifier
370372
}
371373

374+
/**
375+
* A layout qualifier statement.
376+
*/
377+
export interface LayoutQualifierStatement extends Node {
378+
type: 'LayoutQualifierStatement'
379+
layout: Record<string, string | boolean>
380+
qualifier: StorageQualifier
381+
}
382+
372383
export type Expression =
373384
| Literal
374385
| Identifier
@@ -396,11 +407,12 @@ export type Statement =
396407
| ForStatement
397408
| FunctionDeclaration
398409
| VariableDeclaration
399-
| UniformDeclarationBlock
410+
| StructuredBufferDeclaration
400411
| StructDeclaration
401412
| PreprocessorStatement
402-
| PrecisionStatement
403-
| InvariantStatement
413+
| PrecisionQualifierStatement
414+
| InvariantQualifierStatement
415+
| LayoutQualifierStatement
404416

405417
export type AST =
406418
| Program

src/constants.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,95 @@ export const GLSL_KEYWORDS = [
835835
// OCULUS_multiview https://github.com/KhronosGroup/WebGL/issues/2912
836836
'gl_ViewID_OVR',
837837
'GL_OVR_multiview2',
838+
839+
// GLSL ES 3.1
840+
// https://registry.khronos.org/OpenGL/specs/es/3.1/GLSL_ES_Specification_3.10.pdf
841+
842+
// 4.3 Storage Qualifiers
843+
'buffer',
844+
'shared',
845+
846+
// 7.1.2 Fragment Shader Special Variables
847+
'gl_HelperInvocation',
848+
849+
// 7.1.3 Compute Shader Special Variables
850+
'gl_NumWorkGroups',
851+
'gl_WorkGroupSize',
852+
'gl_WorkGroupID',
853+
'gl_LocalInvocationID',
854+
'gl_GlobalInvocationID',
855+
'gl_LocalInvocationIndex',
856+
857+
// 7.2 Built-In Constants
858+
'gl_MaxImageUnits',
859+
'gl_MaxVertexImageUniforms',
860+
'gl_MaxFragmentImageUniforms',
861+
'gl_MaxComputeImageUniforms',
862+
'gl_MaxCombinedImageUniforms',
863+
'gl_MaxCombinedShaderOutputResources',
864+
'gl_MaxComputeWorkGroupCount',
865+
'gl_MaxComputeWorkGroupSize',
866+
'gl_MaxComputeUniformComponents',
867+
'gl_MaxComputeTextureImageUnits',
868+
'gl_MaxComputeAtomicCounters',
869+
'gl_MaxComputeAtomicCounterBuffers',
870+
'gl_MaxVertexAtomicCounters',
871+
'gl_MaxFragmentAtomicCounters',
872+
'gl_MaxCombinedAtomicCounters',
873+
'gl_MaxAtomicCounterBindings',
874+
'gl_MaxFragmentAtomicCounterBuffers',
875+
'gl_MaxVertexAtomicCounterBuffers',
876+
'gl_MaxCombinedAtomicCounterBuffers',
877+
'gl_MaxAtomicCounterBufferSize',
878+
879+
// 8 Built-in Functions
880+
'imageSize',
881+
'packUnorm4x8',
882+
'packSnorm4x8',
883+
'unpackUnorm4x8',
884+
'unpackSnorm4x8',
885+
886+
// 8.8 Integer Functions
887+
'bitfieldExtract',
888+
'bitfieldInsert',
889+
'bitfieldReverse',
890+
'bitCount',
891+
'findLSB',
892+
'findMSB',
893+
'uaddCarry',
894+
'usubBorrow',
895+
'umulExtended',
896+
'imulExtended',
897+
898+
// 8.10 Atomic-Counter Functions
899+
'atomicCounterIncrement',
900+
'atomicCounterDecrement',
901+
'atomicCounter',
902+
903+
// 8.11 Atomic Memory Functions
904+
'atomicAdd',
905+
'atomicMin',
906+
'atomicMax',
907+
'atomicAnd',
908+
'atomicOr',
909+
'atomicXor',
910+
'atomicExchange',
911+
'atomicCompSwap',
912+
913+
// 8.12 Image Functions
914+
'imageLoad',
915+
'imageStore',
916+
917+
// 8.14 Shader Invocation Control Functions
918+
'barrier',
919+
920+
// 8.15 Shader Memory Control Functions
921+
'memoryBarrier',
922+
'memoryBarrierAtomicCounter',
923+
'memoryBarrierBuffer',
924+
'memoryBarrierImage',
925+
'memoryBarrierShared',
926+
'groupMemoryBarrier',
838927
]
839928

840929
const SYMBOLS = [

src/generator.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ function format(node: AST | null): string {
1818
case 'Literal':
1919
return node.value
2020
case 'ArraySpecifier':
21-
return `${node.typeSpecifier.name}[${node.dimensions.map(format).join(',')}]`
21+
return `${node.typeSpecifier.name}${node.dimensions.map((d) => `[${format(d)}]`).join('')}`
2222
case 'ExpressionStatement':
2323
return `${format(node.expression)};`
2424
case 'BlockStatement':
@@ -29,16 +29,18 @@ function format(node: AST | null): string {
2929
let value = ''
3030
if (node.value) {
3131
if (node.name === 'include') value = ` <${format(node.value[0])}>` // three is whitespace sensitive
32-
else if (node.name === 'extension') value = `${node.value.map(format).join(':')}`
33-
else value = ` ${node.value.map(format).join(' ')}`
32+
else if (node.name === 'extension') value = ` ${node.value.map(format).join(':')}`
33+
else if (node.value.length) value = ` ${node.value.map(format).join(' ')}`
3434
}
3535

3636
return `\n#${node.name}${value}\n`
3737
}
38-
case 'PrecisionStatement':
38+
case 'PrecisionQualifierStatement':
3939
return `precision ${node.precision} ${node.typeSpecifier.name};`
40-
case 'InvariantStatement':
40+
case 'InvariantQualifierStatement':
4141
return `invariant ${format(node.typeSpecifier)};`
42+
case 'LayoutQualifierStatement':
43+
return `${formatLayout(node.layout)}${node.qualifier};`
4244
case 'ReturnStatement':
4345
return node.argument ? `return ${format(node.argument)};` : 'return;'
4446
case 'BreakStatement':
@@ -68,7 +70,8 @@ function format(node: AST | null): string {
6870
}
6971
case 'FunctionParameter': {
7072
const qualifiers = node.qualifiers.length ? `${node.qualifiers.join(' ')} ` : ''
71-
return `${qualifiers}${format(node.typeSpecifier)} ${format(node.id)}`
73+
const id = node.id ? ` ${format(node.id)}` : ''
74+
return `${qualifiers}${format(node.typeSpecifier)}${id}`
7275
}
7376
case 'VariableDeclaration': {
7477
const head = node.declarations[0]
@@ -80,11 +83,12 @@ function format(node: AST | null): string {
8083
const init = node.init ? `=${format(node.init)}` : ''
8184
return `${format(node.id)}${init}`
8285
}
83-
case 'UniformDeclarationBlock': {
86+
case 'StructuredBufferDeclaration': {
8487
const layout = formatLayout(node.layout)
85-
const qualifiers = node.qualifiers.length ? `${node.qualifiers.join(' ')} ` : ''
8688
const scope = node.id ? `${format(node.id)}` : ''
87-
return `${layout}${qualifiers}${format(node.typeSpecifier)}{${node.members.map(format).join('')}}${scope};`
89+
return `${layout}${node.qualifiers.join(' ')} ${format(node.typeSpecifier)}{${node.members
90+
.map(format)
91+
.join('')}}${scope};`
8892
}
8993
case 'StructDeclaration':
9094
return `struct ${format(node.id)}{${node.members.map(format).join('')}};`
@@ -120,5 +124,5 @@ export interface GenerateOptions {
120124
* Generates a string of GLSL (WGSL WIP) code from an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
121125
*/
122126
export function generate(program: Program, options: GenerateOptions): string {
123-
return format(program).replaceAll('\n\n', '\n').trim()
127+
return format(program).replaceAll('\n\n', '\n').replaceAll('] ', ']').trim()
124128
}

src/minifier.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,21 @@ const isStorage = /* @__PURE__ */ RegExp.prototype.test.bind(
1919
/^(binding|group|layout|uniform|in|out|attribute|varying)$/,
2020
)
2121

22+
const NEWLINE_REGEX = /\\\s+/gm
23+
const DIRECTIVE_REGEX = /(^\s*#[^\\]*?)(\n|\/[\/\*])/gm
24+
2225
/**
2326
* Minifies a string of GLSL or WGSL code.
2427
*/
2528
export function minify(
2629
code: string,
2730
{ mangle = false, mangleMap = new Map(), mangleExternals = false }: Partial<MinifyOptions> = {},
2831
): string {
32+
// Fold newlines
33+
code = code.replace(NEWLINE_REGEX, '')
34+
2935
// Escape newlines after directives, skip comments
30-
code = code.replace(/(^\s*#[^\\]*?)(\n|\/[\/\*])/gm, '$1\\$2')
36+
code = code.replace(DIRECTIVE_REGEX, '$1\\$2')
3137

3238
const mangleCache = new Map()
3339
const tokens: Token[] = tokenize(code).filter((token) => token.type !== 'whitespace' && token.type !== 'comment')

0 commit comments

Comments
 (0)