Skip to content

Commit 84950e2

Browse files
committed
fix: Choose abstractFloat over i32 in conversion
1 parent 17b7556 commit 84950e2

4 files changed

Lines changed: 51 additions & 4 deletions

File tree

packages/typegpu/src/tgsl/conversion.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { stitch } from '../core/resolve/stitch.ts';
22
import { UnknownData } from '../data/dataTypes.ts';
33
import { undecorate } from '../data/dataTypes.ts';
4+
import { f32 } from '../data/numeric.ts';
45
import { derefSnippet, RefOperator } from '../data/ref.ts';
56
import { schemaCallWrapperGPU } from '../data/schemaCallWrapper.ts';
67
import { snip, type Snippet } from '../data/snippet.ts';
@@ -120,12 +121,18 @@ function getImplicitConversionRank(src: BaseData, dest: BaseData): ConversionRan
120121
}
121122
}
122123

124+
if (trueSrc.type in primitivePreference && trueDst.type === 'abstractFloat') {
125+
// When one of the types is a float (abstract or not), we don't want to cast it to a non-float type,
126+
// which would cause it to lose precision. We instead choose the common type to be f32.
127+
return { rank: 1, action: 'cast', targetType: f32 };
128+
}
129+
123130
if (trueSrc.type === 'abstractFloat') {
124131
if (trueDst.type === 'u32') {
125-
return { rank: 2, action: 'cast', targetType: trueDst };
132+
return { rank: 3, action: 'cast', targetType: trueDst };
126133
}
127134
if (trueDst.type === 'i32') {
128-
return { rank: 1, action: 'cast', targetType: trueDst };
135+
return { rank: 2, action: 'cast', targetType: trueDst };
129136
}
130137
}
131138

@@ -167,6 +174,10 @@ function findBestType(
167174
let bestResult: { type: BaseData; details: ConversionRankInfo[]; sum: number } | undefined;
168175

169176
for (const targetType of uniqueTypes) {
177+
/**
178+
* The type we end up converting to. Will be different than `targetType` if `targetType === abstractFloat`
179+
*/
180+
let destType = targetType;
170181
const details: ConversionRankInfo[] = [];
171182
let sum = 0;
172183
for (const sourceType of types) {
@@ -176,9 +187,12 @@ function findBestType(
176187
break;
177188
}
178189
details.push(conversion);
190+
if (conversion.action === 'cast') {
191+
destType = conversion.targetType;
192+
}
179193
}
180194
if (sum < (bestResult?.sum ?? Number.POSITIVE_INFINITY)) {
181-
bestResult = { type: targetType, details, sum };
195+
bestResult = { type: destType, details, sum };
182196
}
183197
}
184198
if (!bestResult) {

packages/typegpu/tests/indent.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ describe('indents', () => {
333333
})((input) => {
334334
'use gpu';
335335
const uniBoid = layout.$.boids;
336-
for (let i = d.u32(); i < std.floor(std.sin(Math.PI / 2)); i++) {
336+
for (let i = d.u32(); i < d.u32(std.sin(Math.PI / 2)); i++) {
337337
const sampled = std.textureSample(layout.$.sampled, layout.$.sampler, d.vec2f(0.5, 0.5), i);
338338
const someVal = std.textureLoad(layout.$.smoothRender, d.vec2i(), 0);
339339
if (someVal.x + sampled.x > 0.5) {

packages/typegpu/tests/tgsl/conversion.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,12 @@ describe('convertToCommonType', () => {
260260
expect(result).toBeUndefined();
261261
});
262262

263+
it('chooses abstractFloat over i32', () => {
264+
const result = convertToCommonType(ctx, [snippetI32, snippetAbsFloat]);
265+
expect(result).toBeDefined();
266+
expect(result?.[0]?.dataType.type).toBe('f32');
267+
});
268+
263269
it('respects restrictTo types', () => {
264270
// [abstractInt, i32] -> common type i32
265271
// Restrict to f32: requires cast for i32
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { test } from 'typegpu-testing-utility';
2+
import { expect } from 'vitest';
3+
import tgpu, { d } from 'typegpu';
4+
import { expectSnippetOf } from '../utils/parseResolved.ts';
5+
6+
test('multiplication', () => {
7+
function main() {
8+
'use gpu';
9+
const a = d.i32(1) * 0.001;
10+
const int = d.i32(1);
11+
return int * 0.001;
12+
// return foo(d.vec3i(1, 2, 3));
13+
}
14+
15+
expect(tgpu.resolve([main])).toMatchInlineSnapshot(`
16+
"fn main() -> f32 {
17+
const a = 1e-3f;
18+
const int = 1i;
19+
return (f32(int) * 1e-3f);
20+
}"
21+
`);
22+
23+
expectSnippetOf(() => {
24+
'use gpu';
25+
return d.i32(1) * 0.001;
26+
}).toStrictEqual([0.001, d.f32, 'constant']);
27+
});

0 commit comments

Comments
 (0)