@@ -56,6 +56,7 @@ export abstract class Op {
5656 public dependsOn : Op [ ]
5757 public usedIn : Op [ ] = [ ]
5858 public srcLine : string = ''
59+ public internalDerivatives : { op : Op , param : Param } [ ] = [ ]
5960
6061 constructor ( ad : ADBase , ...params : Op [ ] ) {
6162 this . ad = ad
@@ -106,7 +107,7 @@ export abstract class Op {
106107
107108 public derivRef ( param : Param ) : string {
108109 if ( this . useTempVar ( ) ) {
109- return `_glslad_dv${ this . id } _d${ param . name } `
110+ return `_glslad_dv${ this . id } _d${ param . safeName ( ) } `
110111 } else {
111112 return `(${ this . derivative ( param ) } )`
112113 }
@@ -137,15 +138,58 @@ export abstract class Op {
137138 return this . dependsOn . every ( ( op ) => op . isConst ( ) )
138139 }
139140
140- public deepDependencies ( ) : Set < Op > {
141- const deps = new Set < Op > ( )
141+ public outputDependencies ( { deps , derivDeps } : { deps : Set < Op > ; derivDeps : Map < Param , Set < Op > > } ) : string {
142+ let code = ''
142143 for ( const op of this . dependsOn ) {
143- for ( const dep of op . deepDependencies ( ) . values ( ) ) {
144- deps . add ( dep )
144+ if ( ! deps . has ( op ) ) {
145+ deps . add ( op )
146+ code += op . outputDependencies ( { deps, derivDeps } )
147+ code += op . initializer ( )
145148 }
146- deps . add ( op )
147149 }
148- return deps
150+
151+ for ( const { param, op } of this . internalDerivatives ) {
152+ if ( ! derivDeps . get ( param ) ?. has ( op ) ) {
153+ const paramDerivDeps = derivDeps . get ( param ) ?? new Set < Op > ( )
154+ paramDerivDeps . add ( op )
155+ derivDeps . set ( param , paramDerivDeps )
156+ code += op . outputDerivDependencies ( param , { deps, derivDeps } )
157+ code += op . derivInitializer ( param )
158+ }
159+ }
160+
161+ return code
162+ }
163+
164+ public outputDerivDependencies ( param : Param , { deps, derivDeps } : { deps : Set < Op > ; derivDeps : Map < Param , Set < Op > > } ) : string {
165+ let code = ''
166+ for ( const op of this . dependsOn ) {
167+ if ( ! deps . has ( op ) ) {
168+ deps . add ( op )
169+ code += op . outputDependencies ( { deps, derivDeps } )
170+ code += op . initializer ( )
171+ }
172+
173+ if ( ! derivDeps . get ( param ) ?. has ( op ) ) {
174+ const paramDerivDeps = derivDeps . get ( param ) ?? new Set < Op > ( )
175+ paramDerivDeps . add ( op )
176+ derivDeps . set ( param , paramDerivDeps )
177+ code += op . outputDerivDependencies ( param , { deps, derivDeps } )
178+ code += op . derivInitializer ( param )
179+ }
180+ }
181+
182+ for ( const { param, op } of this . internalDerivatives ) {
183+ if ( ! derivDeps . get ( param ) ?. has ( op ) ) {
184+ const paramDerivDeps = derivDeps . get ( param ) ?? new Set < Op > ( )
185+ paramDerivDeps . add ( op )
186+ derivDeps . set ( param , paramDerivDeps )
187+ code += op . outputDependencies ( { deps, derivDeps } )
188+ code += op . derivInitializer ( param )
189+ }
190+ }
191+
192+ return code
149193 }
150194
151195 public output ( name : string ) { return this . ad . output ( name , this ) }
@@ -155,6 +199,81 @@ export abstract class Op {
155199 public abstract derivative ( param : Param ) : string
156200}
157201
202+ export abstract class BooleanOp extends Op {
203+ abstract operator ( ) : string
204+ definition ( ) {
205+ return this . dependsOn . map ( ( op ) => op . ref ( ) ) . join ( this . operator ( ) )
206+ }
207+ derivative ( ) : string {
208+ throw new Error ( 'unimplemented' )
209+ }
210+ isConst ( ) {
211+ // They might not actually be constant, but we don't have derivatives
212+ // for these so we just treat them like they are
213+ return true ;
214+ }
215+ glslType ( ) {
216+ return 'bool'
217+ }
218+ }
219+
220+ export class EqOp extends BooleanOp {
221+ operator ( ) {
222+ return '=='
223+ }
224+ }
225+
226+ export class NeOp extends BooleanOp {
227+ operator ( ) {
228+ return '!='
229+ }
230+ }
231+
232+ export class LtOp extends BooleanOp {
233+ operator ( ) {
234+ return '<'
235+ }
236+ }
237+
238+ export class LeOp extends BooleanOp {
239+ operator ( ) {
240+ return '<='
241+ }
242+ }
243+
244+ export class GtOp extends BooleanOp {
245+ operator ( ) {
246+ return '>'
247+ }
248+ }
249+
250+ export class GeOp extends BooleanOp {
251+ operator ( ) {
252+ return '>='
253+ }
254+ }
255+
256+ export class NotOp extends BooleanOp {
257+ operator ( ) {
258+ return '!'
259+ }
260+ definition ( ) {
261+ return this . operator ( ) + this . dependsOn [ 0 ] . ref ( )
262+ }
263+ }
264+
265+ export class AndOp extends BooleanOp {
266+ operator ( ) {
267+ return '&&'
268+ }
269+ }
270+
271+ export class OrOp extends BooleanOp {
272+ operator ( ) {
273+ return '||'
274+ }
275+ }
276+
158277export abstract class OpLiteral extends Op {
159278 public override initializer ( ) { return '' }
160279 public override derivInitializer ( ) { return '' }
@@ -187,6 +306,18 @@ export class Param extends OpLiteral {
187306 this . ad . registerParam ( this , name )
188307 }
189308
309+ safeName ( ) {
310+ // A version of the name that can be used in temp variable
311+ // names
312+ return this . name . split ( '' ) . map ( ( c ) => {
313+ if ( c . match ( / [ \w \d ] / ) ) {
314+ return c
315+ } else {
316+ return '_'
317+ }
318+ } ) . join ( '' ) + this . id // Add id to ensure uniqueness
319+ }
320+
190321 isConst ( ) { return false }
191322 definition ( ) { return this . name }
192323 derivative ( param : Param ) {
0 commit comments