1
- import { ReflectionJSON } from './compiler.js' ;
1
+ import { ReflectionJSON , ReflectionType } from './compiler.js' ;
2
2
import { ParsedCommand } from './try-slang.js' ;
3
3
4
4
export function configContext ( device : GPUDevice , canvas : HTMLCanvasElement ) {
@@ -42,6 +42,42 @@ function reinterpretUint32AsFloat(uint32: number) {
42
42
return float32View [ 0 ] ;
43
43
}
44
44
45
+ function roundUpToNearest ( x : number , nearest : number ) {
46
+ return Math . ceil ( x / nearest ) * nearest ;
47
+ }
48
+
49
+ function getSize ( reflectionType : ReflectionType ) : number {
50
+ if ( reflectionType . kind == "resource" ) {
51
+ throw new Error ( "unimplemented" ) ;
52
+ } else if ( reflectionType . kind == "scalar" ) {
53
+ const bitsMatch = reflectionType . scalarType . match ( / \d + $ / ) ;
54
+ if ( bitsMatch == null ) {
55
+ throw new Error ( "Could not get bit count out of scalar type" ) ;
56
+ }
57
+ return parseInt ( bitsMatch [ 0 ] ) / 8 ;
58
+ } else if ( reflectionType . kind == "struct" ) {
59
+ const alignment = reflectionType . fields . map ( ( f ) => {
60
+ if ( f . binding . kind == "uniform" ) return f . binding . size ;
61
+ else throw new Error ( "Invalid state" )
62
+ } ) . reduce ( ( a , b ) => Math . max ( a , b ) ) ;
63
+
64
+ const unalignedSize = reflectionType . fields . map ( ( f ) => {
65
+ if ( f . binding . kind == "uniform" ) return f . binding . offset + f . binding . size ;
66
+ else throw new Error ( "Invalid state" )
67
+ } ) . reduce ( ( a , b ) => Math . max ( a , b ) ) ;
68
+
69
+ return roundUpToNearest ( unalignedSize , alignment ) ;
70
+ } else if ( reflectionType . kind == "vector" ) {
71
+ if ( reflectionType . elementCount == 3 ) {
72
+ return 4 * getSize ( reflectionType . elementType ) ;
73
+ }
74
+ return reflectionType . elementCount * getSize ( reflectionType . elementType ) ;
75
+ } else {
76
+ let x :never = reflectionType ;
77
+ throw new Error ( "Cannot get size of unrecognized reflection type" ) ;
78
+ }
79
+ }
80
+
45
81
/**
46
82
* Here are some patterns we support:
47
83
*
@@ -63,20 +99,41 @@ export function getCommandsFromAttributes(reflection: ReflectionJSON): { resourc
63
99
if ( ! attribute . name . startsWith ( "playground_" ) ) continue ;
64
100
65
101
let playground_attribute_name = attribute . name . slice ( 11 ) ;
66
- if ( playground_attribute_name == "ZEROS" || playground_attribute_name == "RAND" ) {
102
+ if ( playground_attribute_name == "ZEROS" ) {
103
+ if ( parameter . type . kind != "resource" || parameter . type . baseShape != "structuredBuffer" ) {
104
+ throw new Error ( `ZEROS attribute cannot be applied to ${ parameter . name } , it only supports buffers` )
105
+ }
67
106
command = {
68
107
type : playground_attribute_name ,
69
108
count : attribute . arguments [ 0 ] as number ,
109
+ elementSize : getSize ( parameter . type . resultType ) ,
110
+ } ;
111
+ } else if ( playground_attribute_name == "RAND" ) {
112
+ if ( parameter . type . kind != "resource" || parameter . type . baseShape != "structuredBuffer" ) {
113
+ throw new Error ( `RAND attribute cannot be applied to ${ parameter . name } , it only supports buffers` )
114
+ }
115
+ if ( parameter . type . resultType . kind != "scalar" || parameter . type . resultType . scalarType != "float32" ) {
116
+ throw new Error ( `RAND attribute cannot be applied to ${ parameter . name } , it only supports float buffers` )
117
+ }
118
+ command = {
119
+ type : playground_attribute_name ,
120
+ count : attribute . arguments [ 0 ] as number
70
121
} ;
71
122
} else if ( playground_attribute_name == "BLACK" ) {
123
+ if ( parameter . type . kind != "resource" || parameter . type . baseShape != "texture2D" ) {
124
+ throw new Error ( `BLACK attribute cannot be applied to ${ parameter . name } , it only supports 2D textures` )
125
+ }
72
126
command = {
73
- type : "BLACK" ,
127
+ type : playground_attribute_name ,
74
128
width : attribute . arguments [ 0 ] as number ,
75
129
height : attribute . arguments [ 1 ] as number ,
76
130
} ;
77
131
} else if ( playground_attribute_name == "URL" ) {
132
+ if ( parameter . type . kind != "resource" || parameter . type . baseShape != "texture2D" ) {
133
+ throw new Error ( `URL attribute cannot be applied to ${ parameter . name } , it only supports 2D textures` )
134
+ }
78
135
command = {
79
- type : "URL" ,
136
+ type : playground_attribute_name ,
80
137
url : attribute . arguments [ 0 ] as string ,
81
138
} ;
82
139
}
@@ -97,13 +154,14 @@ export type CallCommand = {
97
154
type : "RESOURCE_BASED" ,
98
155
fnName : string ,
99
156
resourceName : string ,
157
+ elementSize ?: number ,
100
158
} | {
101
159
type : "FIXED_SIZE" ,
102
160
fnName : string ,
103
161
size : number [ ] ,
104
162
} ;
105
163
106
- export function parseCallCommands ( userSource : string ) : CallCommand [ ] {
164
+ export function parseCallCommands ( userSource : string , reflection : ReflectionJSON ) : CallCommand [ ] {
107
165
// Look for commands of the form:
108
166
//
109
167
// 1. //! CALL(fn-name, SIZE_OF(<resource-name>)) ==> Dispatch a compute pass with the given
@@ -122,7 +180,16 @@ export function parseCallCommands(userSource: string): CallCommand[] {
122
180
const args = match [ 2 ] . split ( ',' ) . map ( arg => arg . trim ( ) ) ;
123
181
124
182
if ( args [ 0 ] . startsWith ( "SIZE_OF" ) ) {
125
- callCommands . push ( { type : "RESOURCE_BASED" , fnName, resourceName : args [ 0 ] . slice ( 8 , - 1 ) } ) ;
183
+ let resourceName = args [ 0 ] . slice ( 8 , - 1 ) ;
184
+ let resourceReflection = reflection . parameters . find ( ( param ) => param . name == resourceName ) ;
185
+ if ( resourceReflection == undefined ) {
186
+ throw new Error ( `Cannot find resource ${ resourceName } for ${ fnName } CALL command` )
187
+ }
188
+ let elementSize : number | undefined = undefined ;
189
+ if ( resourceReflection . type . kind == "resource" && resourceReflection . type . baseShape == "structuredBuffer" ) {
190
+ elementSize = getSize ( resourceReflection . type . resultType ) ;
191
+ }
192
+ callCommands . push ( { type : "RESOURCE_BASED" , fnName, resourceName, elementSize } ) ;
126
193
}
127
194
else {
128
195
callCommands . push ( { type : "FIXED_SIZE" , fnName, size : args . map ( Number ) } ) ;
0 commit comments