Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions packages/typegpu/src/core/rawCodeSnippet/tgpuRawCodeSnippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,25 @@ class TgpuRawCodeSnippetImpl<TDataType extends BaseData> implements TgpuRawCodeS
this.#expression,
);

return snip(replacedExpression, this.dataType, this.origin, this.possibleSideEffects);
return snip(
replacedExpression,
this.dataType,
this.origin,
this.possibleSideEffects,
/* mayDeclareVariables */ true,
);
},
}),
{
codegenMode: {
getBaseSnippet(trackingProxy) {
return snip(trackingProxy, this.dataType, this.origin, this.possibleSideEffects);
return snip(
trackingProxy,
this.dataType,
this.origin,
this.possibleSideEffects,
/* mayDeclareVariables */ true,
);
},
},
normalMode: {
Expand Down
37 changes: 34 additions & 3 deletions packages/typegpu/src/data/snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ export interface Snippet {
* that mutates memory, or synchronizes threads, has side effects.
*/
readonly possibleSideEffects: boolean;
/**
* Whether opaque source code may introduce names in the surrounding scope.
* Statement blocks containing such snippets must not be inlined.
*/
readonly mayDeclareVariables?: true;
}

export interface ResolvedSnippet extends Snippet {
Expand All @@ -123,11 +128,15 @@ class SnippetImpl implements Snippet {
dataType: BaseData | UnknownData,
origin: Origin,
possibleSideEffects: boolean,
mayDeclareVariables: boolean,
) {
this.value = value;
this.dataType = dataType;
this.origin = origin;
this.possibleSideEffects = possibleSideEffects;
if (mayDeclareVariables) {
Object.defineProperty(this, 'mayDeclareVariables', { value: true });
}
}
}

Expand All @@ -144,18 +153,21 @@ export function snip(
dataType: BaseData,
origin: Origin,
possibleSideEffects?: boolean,
mayDeclareVariables?: boolean,
): ResolvedSnippet;
export function snip(
value: unknown,
dataType: BaseData | UnknownData,
origin: Origin,
possibleSideEffects?: boolean,
mayDeclareVariables?: boolean,
): Snippet;
export function snip(
value: unknown,
dataType: BaseData | UnknownData,
origin: Origin,
possibleSideEffects: boolean = true,
mayDeclareVariables: boolean = false,
): Snippet | ResolvedSnippet {
if (DEV && isSnippet(value)) {
// An early error, but not worth checking every time in production
Expand All @@ -168,6 +180,7 @@ export function snip(
undecorate(dataType as BaseData),
origin,
possibleSideEffects,
mayDeclareVariables,
);
}

Expand All @@ -177,13 +190,25 @@ export function withDataType(
): ResolvedSnippet;
export function withDataType(dataType: BaseData | UnknownData, snippet: Snippet): Snippet;
export function withDataType(dataType: BaseData | UnknownData, snippet: Snippet): Snippet {
return new SnippetImpl(snippet.value, dataType, snippet.origin, snippet.possibleSideEffects);
return new SnippetImpl(
snippet.value,
dataType,
snippet.origin,
snippet.possibleSideEffects,
snippet.mayDeclareVariables ?? false,
);
}

export function withValue(value: string, snippet: Snippet): ResolvedSnippet;
export function withValue(value: unknown, snippet: Snippet): Snippet;
export function withValue(value: unknown, snippet: Snippet): Snippet {
return new SnippetImpl(value, snippet.dataType, snippet.origin, snippet.possibleSideEffects);
return new SnippetImpl(
value,
snippet.dataType,
snippet.origin,
snippet.possibleSideEffects,
snippet.mayDeclareVariables ?? false,
);
}

export function withSideEffects(
Expand All @@ -192,7 +217,13 @@ export function withSideEffects(
): ResolvedSnippet;
export function withSideEffects(possibleSideEffects: boolean, snippet: Snippet): Snippet;
export function withSideEffects(possibleSideEffects: boolean, snippet: Snippet): Snippet {
return new SnippetImpl(snippet.value, snippet.dataType, snippet.origin, possibleSideEffects);
return new SnippetImpl(
snippet.value,
snippet.dataType,
snippet.origin,
possibleSideEffects,
snippet.mayDeclareVariables ?? false,
);
}

export function noSideEffects(snippet: ResolvedSnippet): ResolvedSnippet;
Expand Down
5 changes: 4 additions & 1 deletion packages/typegpu/src/tgsl/wgslGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1833,7 +1833,10 @@ ${this.ctx.pre}else ${alternate}`,
const expr = this._expression(statement);
const resolved =
expr.value !== undefined && expr.value !== null ? this.ctx.resolveSnippet(expr).value : '';
return { code: resolved ? `${this.ctx.pre}${resolved};` : '', definesInNearestScope: false };
return {
code: resolved ? `${this.ctx.pre}${resolved};` : '',
definesInNearestScope: expr.mayDeclareVariables ?? false,
};
}

/**
Expand Down
28 changes: 28 additions & 0 deletions packages/typegpu/tests/unroll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,4 +927,32 @@ describe('tgpu.unroll', () => {
}"
`);
});
it('keeps scopes around unrolled raw code snippets', () => {
const snippet = tgpu['~unstable'].rawCodeSnippet('const value = 1', d.Void);

const main = () => {
'use gpu';
for (const _ of tgpu.unroll([1, 2, 3])) {
snippet.$;
}
};

expect(tgpu.resolve([main])).toMatchInlineSnapshot(`
"fn main() {
// unrolled iteration #0
{
const value = 1;
}
// unrolled iteration #1
{
const value = 1;
}
// unrolled iteration #2
{
const value = 1;
}
// ---
}"
`);
});
});