From 704a81eb9e29b58653b77246f8683ffa661edc58 Mon Sep 17 00:00:00 2001 From: Iwo Plaza Date: Thu, 5 Mar 2026 13:22:32 +0100 Subject: [PATCH] fix: Less hacky vector implementation fix: Less hacky vector implementation --- packages/typegpu/src/core/root/init.ts | 16 +- packages/typegpu/src/data/vectorImpl.ts | 209 ++++++++++++---------- packages/typegpu/src/data/wgslTypes.ts | 6 +- packages/typegpu/tests/compiledIO.test.ts | 2 +- packages/typegpu/tests/vector.test.ts | 7 + 5 files changed, 131 insertions(+), 109 deletions(-) diff --git a/packages/typegpu/src/core/root/init.ts b/packages/typegpu/src/core/root/init.ts index d0fd08b35b..78f7ab8b83 100644 --- a/packages/typegpu/src/core/root/init.ts +++ b/packages/typegpu/src/core/root/init.ts @@ -112,10 +112,10 @@ function toVec3(arr: readonly (number | undefined)[]): v3u { } const workgroupSizeConfigs = [ - vec3u(1, 1, 1), - vec3u(256, 1, 1), - vec3u(16, 16, 1), - vec3u(8, 8, 4), + [1, 1, 1], + [256, 1, 1], + [16, 16, 1], + [8, 8, 4], ] as const; export class TgpuGuardedComputePipelineImpl< @@ -124,15 +124,15 @@ export class TgpuGuardedComputePipelineImpl< #root: ExperimentalTgpuRoot; #pipeline: TgpuComputePipeline; #sizeUniform: TgpuUniform; - #workgroupSize: v3u; + #workgroupSize: [number, number, number]; - #lastSize: v3u; + #lastSize: [number, number, number]; constructor( root: ExperimentalTgpuRoot, pipeline: TgpuComputePipeline, sizeUniform: TgpuUniform, - workgroupSize: v3u, + workgroupSize: [number, number, number], ) { this.#root = root; this.#pipeline = pipeline; @@ -228,7 +228,7 @@ class WithBindingImpl implements WithBinding { throw new Error('Guarded compute callback only supports up to three dimensions.'); } - const workgroupSize = workgroupSizeConfigs[callback.length] as v3u; + const workgroupSize = workgroupSizeConfigs[callback.length] as [number, number, number]; const wrappedCallback = fn([u32, u32, u32])(callback as (...args: number[]) => void); const sizeUniform = root.createUniform(vec3u); diff --git a/packages/typegpu/src/data/vectorImpl.ts b/packages/typegpu/src/data/vectorImpl.ts index ad505aa6b1..9f7163ab95 100644 --- a/packages/typegpu/src/data/vectorImpl.ts +++ b/packages/typegpu/src/data/vectorImpl.ts @@ -9,7 +9,7 @@ type VecSchema = BaseData & ((v?: S) => S); const XYZW = ['x', 'y', 'z', 'w']; const RGBA = ['r', 'g', 'b', 'a']; -export abstract class VecBase extends Array implements SelfResolvable { +export abstract class VecBase implements SelfResolvable { abstract readonly [$internal]: { elementSchema: VecSchema; }; @@ -19,6 +19,9 @@ export abstract class VecBase extends Array implements SelfResolvable { abstract get _Vec3(): new (x: S, y: S, z: S) => Vec3; abstract get _Vec4(): new (x: S, y: S, z: S, w: S) => Vec4; + abstract readonly length: number; + [n: number]: S; + static { // Defining 4-length swizzles for (let e0 = 0; e0 < 4; e0++) { @@ -85,15 +88,22 @@ export abstract class VecBase extends Array implements SelfResolvable { return this[$internal].elementSchema; } + *[Symbol.iterator]() { + for (let i = 0; i < this.length; i++) { + yield this[i]; + } + } + [$resolve](): ResolvedSnippet { const schema = this[$internal].elementSchema; - if (this.every((e) => !e)) { + const arr = [...this]; + if (arr.every((e) => !e)) { return snip(`${this.kind}()`, schema, /* origin */ 'constant'); } - if (this.every((e) => this[0] === e)) { - return snip(`${this.kind}(${this[0]})`, schema, /* origin */ 'runtime'); + if (arr.every((e) => arr[0] === e)) { + return snip(`${this.kind}(${arr[0]})`, schema, /* origin */ 'runtime'); } - return snip(`${this.kind}(${this.join(', ')})`, schema, /* origin */ 'runtime'); + return snip(`${this.kind}(${arr.join(', ')})`, schema, /* origin */ 'runtime'); } toString() { @@ -101,267 +111,272 @@ export abstract class VecBase extends Array implements SelfResolvable { } } -type Tuple2 = [S, S]; -type Tuple3 = [S, S, S]; -type Tuple4 = [S, S, S, S]; - -abstract class Vec2 extends VecBase implements Tuple2 { +abstract class Vec2 extends VecBase { declare readonly length: 2; + static { + Object.defineProperty(Vec2.prototype, 'length', { value: 2 }); + } - e0: S; - e1: S; + #e0: S; + #e1: S; constructor(x?: S, y?: S) { - super(2); - this.e0 = this.castElement()(x); - this.e1 = this.castElement()(y ?? x); + super(); + this.#e0 = this.castElement()(x); + this.#e1 = this.castElement()(y ?? x); } get 0() { - return this.e0; + return this.#e0; } get 1() { - return this.e1; + return this.#e1; } set 0(value: S) { - this.e0 = this.castElement()(value); + this.#e0 = this.castElement()(value); } set 1(value: S) { - this.e1 = this.castElement()(value); + this.#e1 = this.castElement()(value); } get x() { - return this[0]; + return this.#e0; } get y() { - return this[1]; + return this.#e1; } set x(value: S) { - this[0] = this.castElement()(value); + this[0] = value; } set y(value: S) { - this[1] = this.castElement()(value); + this[1] = value; } get r() { - return this[0]; + return this.#e0; } get g() { - return this[1]; + return this.#e1; } set r(value: S) { - this[0] = this.castElement()(value); + this[0] = value; } set g(value: S) { - this[1] = this.castElement()(value); + this[1] = value; } } -abstract class Vec3 extends VecBase implements Tuple3 { +abstract class Vec3 extends VecBase { declare readonly length: 3; + static { + Object.defineProperty(Vec3.prototype, 'length', { value: 3 }); + } - e0: S; - e1: S; - e2: S; + #e0: S; + #e1: S; + #e2: S; constructor(x?: S, y?: S, z?: S) { - super(3); - this.e0 = this.castElement()(x); - this.e1 = this.castElement()(y ?? x); - this.e2 = this.castElement()(z ?? x); + super(); + this.#e0 = this.castElement()(x); + this.#e1 = this.castElement()(y ?? x); + this.#e2 = this.castElement()(z ?? x); } get 0() { - return this.e0; + return this.#e0; } get 1() { - return this.e1; + return this.#e1; } get 2() { - return this.e2; + return this.#e2; } set 0(value: S) { - this.e0 = this.castElement()(value); + this.#e0 = this.castElement()(value); } set 1(value: S) { - this.e1 = this.castElement()(value); + this.#e1 = this.castElement()(value); } set 2(value: S) { - this.e2 = this.castElement()(value); + this.#e2 = this.castElement()(value); } get x() { - return this[0]; + return this.#e0; } get y() { - return this[1]; + return this.#e1; } get z() { - return this[2]; + return this.#e2; } set x(value: S) { - this[0] = this.castElement()(value); + this[0] = value; } set y(value: S) { - this[1] = this.castElement()(value); + this[1] = value; } set z(value: S) { - this[2] = this.castElement()(value); + this[2] = value; } get r() { - return this[0]; + return this.#e0; } get g() { - return this[1]; + return this.#e1; } get b() { - return this[2]; + return this.#e2; } set r(value: S) { - this[0] = this.castElement()(value); + this[0] = value; } set g(value: S) { - this[1] = this.castElement()(value); + this[1] = value; } set b(value: S) { - this[2] = this.castElement()(value); + this[2] = value; } } -abstract class Vec4 extends VecBase implements Tuple4 { +abstract class Vec4 extends VecBase { declare readonly length: 4; + static { + Object.defineProperty(Vec4.prototype, 'length', { value: 4 }); + } - e0: S; - e1: S; - e2: S; - e3: S; + #e0: S; + #e1: S; + #e2: S; + #e3: S; constructor(x?: S, y?: S, z?: S, w?: S) { - super(4); - this.e0 = this.castElement()(x); - this.e1 = this.castElement()(y ?? x); - this.e2 = this.castElement()(z ?? x); - this.e3 = this.castElement()(w ?? x); + super(); + this.#e0 = this.castElement()(x); + this.#e1 = this.castElement()(y ?? x); + this.#e2 = this.castElement()(z ?? x); + this.#e3 = this.castElement()(w ?? x); } get 0() { - return this.e0; + return this.#e0; } get 1() { - return this.e1; + return this.#e1; } get 2() { - return this.e2; + return this.#e2; } get 3() { - return this.e3; + return this.#e3; } set 0(value: S) { - this.e0 = this.castElement()(value); + this.#e0 = this.castElement()(value); } set 1(value: S) { - this.e1 = this.castElement()(value); + this.#e1 = this.castElement()(value); } set 2(value: S) { - this.e2 = this.castElement()(value); + this.#e2 = this.castElement()(value); } set 3(value: S) { - this.e3 = this.castElement()(value); + this.#e3 = this.castElement()(value); } get x() { - return this[0]; + return this.#e0; } get y() { - return this[1]; - } - - get r() { - return this[0]; + return this.#e1; } - get g() { - return this[1]; - } - - get b() { - return this[2]; + get z() { + return this.#e2; } - get a() { - return this[3]; + get w() { + return this.#e3; } - set r(value: S) { + set x(value: S) { this[0] = value; } - set g(value: S) { + set y(value: S) { this[1] = value; } - set b(value: S) { + set z(value: S) { this[2] = value; } - set a(value: S) { + set w(value: S) { this[3] = value; } - get z() { - return this[2]; + get r() { + return this.#e0; } - get w() { - return this[3]; + get g() { + return this.#e1; } - set x(value: S) { + get b() { + return this.#e2; + } + + get a() { + return this.#e3; + } + + set r(value: S) { this[0] = value; } - set y(value: S) { + set g(value: S) { this[1] = value; } - set z(value: S) { + set b(value: S) { this[2] = value; } - set w(value: S) { + set a(value: S) { this[3] = value; } } diff --git a/packages/typegpu/src/data/wgslTypes.ts b/packages/typegpu/src/data/wgslTypes.ts index 19784a3b8d..b89ff34f40 100644 --- a/packages/typegpu/src/data/wgslTypes.ts +++ b/packages/typegpu/src/data/wgslTypes.ts @@ -160,9 +160,9 @@ type Swizzle4 = { readonly [K in `${XYZW}${XYZW}${XYZW}${XYZW}` | `${RGBA}${RGBA}${RGBA}${RGBA}`]: T4; }; -type Tuple2 = [S, S]; -type Tuple3 = [S, S, S]; -type Tuple4 = [S, S, S, S]; +type Tuple2 = { 0: S; 1: S }; +type Tuple3 = { 0: S; 1: S; 2: S }; +type Tuple4 = { 0: S; 1: S; 2: S; 3: S }; /** * A type which every numeric vector is assignable to. In most cases the union v2f | v3f | v4f | v2h | v3h | v4h | v2i | v3i | v4i | v2u | v3u | v4u diff --git a/packages/typegpu/tests/compiledIO.test.ts b/packages/typegpu/tests/compiledIO.test.ts index 265f540186..60491178dc 100644 --- a/packages/typegpu/tests/compiledIO.test.ts +++ b/packages/typegpu/tests/compiledIO.test.ts @@ -618,7 +618,7 @@ describe('createCompileInstructions', () => { writer(dataView, 0, inputData); const decoded = { - a: d.vec2f(dataView.getUint16(0, true), dataView.getUint16(2, true)), + a: d.vec2u(dataView.getUint16(0, true), dataView.getUint16(2, true)), b: (() => { const packed = dataView.getUint32(4, true); return d.vec4f( diff --git a/packages/typegpu/tests/vector.test.ts b/packages/typegpu/tests/vector.test.ts index 87149c62a0..9954e04f93 100644 --- a/packages/typegpu/tests/vector.test.ts +++ b/packages/typegpu/tests/vector.test.ts @@ -198,6 +198,13 @@ describe('vec2i', () => { expectTypeOf(x).toBeNumber(); expectTypeOf(y).toBeNumber(); }); + + it('can be spread', () => { + const vec = d.vec2i(5, 6); + const result = [...vec]; + expectTypeOf(result).toEqualTypeOf(); + expect(result).toStrictEqual([5, 6]); + }); }); describe('vec2', () => {