Skip to content

Commit

Permalink
Add pp.join([...]), which joins an array of string with vertical alig…
Browse files Browse the repository at this point in the history
…nment

And use pp in a few places.

Fixes gpuweb#356
  • Loading branch information
kainino0x committed Nov 18, 2020
1 parent 12581ea commit 7b2797d
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 12 deletions.
25 changes: 23 additions & 2 deletions src/common/framework/preprocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ abstract class Directive {
protected checkDepth(stack: StateStack): void {
assert(
stack.length === this.depth,
`Number of "$"s must match nesting depth, currently ${stack.length} (e.g. $if $$if $$endif $endif)`
`Number of "_"s must match nesting depth, currently ${stack.length} (e.g. _if __if __endif _endif)`
);
}

Expand Down Expand Up @@ -87,6 +87,18 @@ class EndIf extends Directive {
}
}

class Join {
private lines: string[];

constructor(lines: string[]) {
this.lines = lines;
}

join(indentation: number) {
return this.lines.join('\n' + ' '.repeat(indentation));
}
}

/**
* A simple template-based, non-line-based preprocessor implementing if/elif/else/endif.
*
Expand All @@ -104,10 +116,13 @@ class EndIf extends Directive {
*
* @param strings - The array of constant string chunks of the template string.
* @param ...values - The array of interpolated ${} values within the template string.
* If a value is a Directive, it affects the preprocessor state and injects no string.
* If a value is a string[], the array is joined together with newlines, preserving vertical
* alignment by indenting with spaces.
*/
export function pp(
strings: TemplateStringsArray,
...values: ReadonlyArray<Directive | string | number>
...values: ReadonlyArray<Directive | Join | string | number>
): string {
let result = '';
const stateStack: StateStack = [{ allowsFollowingElse: false, state: State.Passing }];
Expand All @@ -121,6 +136,11 @@ export function pp(
const value = values[i];
if (value instanceof Directive) {
value.applyTo(stateStack);
} else if (value instanceof Join) {
const indexOfLastNewline = result.lastIndexOf('\n');
// Vertically align by indenting with spaces. Works even if indexOfLastNewline === -1.
const indentation = result.length - indexOfLastNewline - 1;
result += value.join(indentation);
} else {
if (passing) {
result += value;
Expand All @@ -132,6 +152,7 @@ export function pp(

return result;
}
pp.join = (args: string[]) => new Join(args);
pp._if = (predicate: boolean) => new If(1, predicate);
pp._elif = (predicate: boolean) => new ElseIf(1, predicate);
pp._else = new Else(1);
Expand Down
29 changes: 29 additions & 0 deletions src/unittests/preprocessor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,35 @@ b`;
t.test(act, exp);
});

g.test('join,0').fn(t => {
const act = pp`a ${pp.join([])} b`;
const exp = 'a b';
t.test(act, exp);
});

g.test('join,1').fn(t => {
const act = pp`a ${pp.join(['3'])} b`;
const exp = 'a 3 b';
t.test(act, exp);
});

g.test('join,2').fn(t => {
const act = pp`a ${pp.join(['3', '4'])} b`;
const exp = `\
a 3
4 b`;
t.test(act, exp);
});

g.test('join,22').fn(t => {
const act = pp`a ${pp.join(['33333', '4'])} b ${pp.join(['5', '66'])} d`;
const exp = `\
a 33333
4 b 5
66 d`;
t.test(act, exp);
});

g.test('if,true').fn(t => {
const act = pp`
a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Test Coverage:
`;

import { pbool, poptions, params } from '../../../../common/framework/params_builder.js';
import { pp } from '../../../../common/framework/preprocessor.js';
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert } from '../../../../common/framework/util/util.js';
import {
Expand Down Expand Up @@ -925,9 +926,9 @@ g.test('unused_bindings_in_pipeline')
entry_point vertex = main;
`;
// TODO: revisit the shader code once 'image' can be supported in wgsl.
const wgslFragment = `
${useBindGroup0 ? '[[set 0, binding 0]] var<image> image0;' : ''}
${useBindGroup1 ? '[[set 1, binding 0]] var<image> image1;' : ''}
const wgslFragment = pp`
${pp._if(useBindGroup0)}[[set 0, binding 0]] var<image> image0;${pp._endif}
${pp._if(useBindGroup1)}[[set 1, binding 0]] var<image> image1;${pp._endif}
fn main() -> void {
return;
}
Expand All @@ -937,8 +938,8 @@ g.test('unused_bindings_in_pipeline')

// TODO: revisit the shader code once 'image' can be supported in wgsl.
const wgslCompute = `
${useBindGroup0 ? '[[set 0, binding 0]] var<image> image0;' : ''}
${useBindGroup1 ? '[[set 1, binding 0]] var<image> image1;' : ''}
${pp._if(useBindGroup0)}[[set 0, binding 0]] var<image> image0;${pp._endif}
${pp._if(useBindGroup1)}[[set 1, binding 0]] var<image> image1;${pp._endif}
fn main() -> void {
return;
}
Expand Down
9 changes: 4 additions & 5 deletions src/webgpu/util/texture/texelData.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const description = 'Test helpers for texel data produce the expected data in the shader';

import { params, poptions } from '../../../common/framework/params_builder.js';
import { pp } from '../../../common/framework/preprocessor.js';
import { makeTestGroup } from '../../../common/framework/test_group.js';
import { unreachable, assert } from '../../../common/framework/util/util.js';
import {
Expand Down Expand Up @@ -68,20 +69,18 @@ function doTest(
unreachable();
}

const shader = `
const shader = pp`
[[set(0), binding(0)]] var<uniform_constant> tex : texture_2d<${shaderType}>;
[[block]] struct Output {
${rep.componentOrder
.map((C, i) => `[[offset(${i * 4})]] result${C} : ${shaderType};`)
.join('\n')}
${pp.join(rep.componentOrder.map((C, i) => `[[offset(${i * 4})]] result${C} : ${shaderType};`))}
};
[[set(0), binding(1)]] var<storage_buffer> output : Output;
[[stage(compute)]]
fn main() -> void {
var texel : vec4<${shaderType}> = textureLoad(tex, vec2<i32>(0, 0), 0);
${rep.componentOrder.map(C => `output.result${C} = texel.${C.toLowerCase()};`).join('\n')}
${pp.join(rep.componentOrder.map(C => `output.result${C} = texel.${C.toLowerCase()};`))}
return;
}`;

Expand Down

0 comments on commit 7b2797d

Please sign in to comment.