Skip to content

Commit

Permalink
feat(vector-pools): add GLType alias, AttribPoolOpts, update pool impls
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Nov 30, 2018
1 parent 019c0af commit 4fe2047
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 37 deletions.
63 changes: 56 additions & 7 deletions packages/vector-pools/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import { IRelease, TypedArray } from "@thi.ng/api/api";
import { Type } from "@thi.ng/malloc/api";
import { Type, MemPoolOpts } from "@thi.ng/malloc/api";
import { StridedVec, Vec, ReadonlyVec } from "@thi.ng/vectors3/api";

export interface AttribSpec {
type: Type;
type: GLType | Type;
size: number;
default: number | ReadonlyVec;
default?: number | ReadonlyVec;
byteOffset: number;
stride?: number;
}

export interface AttribPoolOpts {
resizable: boolean;
mempool: MemPoolOpts;
}

export interface IVecPool extends IRelease {

malloc(size: number, type?: Type): TypedArray;
malloc(size: number, type?: GLType | Type): TypedArray;

mallocWrapped(size: number, stride?: number, type?: Type): StridedVec;
mallocWrapped(size: number, stride?: number, type?: GLType | Type): StridedVec;

mallocArray(num: number, size: number, cstride?: number, estride?: number, type?: Type): StridedVec[];
mallocArray(num: number, size: number, cstride?: number, estride?: number, type?: GLType | Type): StridedVec[];

free(vec: StridedVec | TypedArray): boolean;

Expand All @@ -26,4 +31,48 @@ export interface IVecPool extends IRelease {
export type VecFactory =
(buf: Vec, size: number, index: number, stride: number) => StridedVec;

export { Type };
/**
* WebGL numeric type constants. These can be used by classes in this
* package as aliases for thi.ng/malloc's `Type` enum (see `GL2TYPE` LUT
* below), but also then used directly when initializing WebGL buffers
* from given attribute buffer specs.
*
* See `AttribPool` & readme examples for more details.
*/
export const enum GLType {
I8 = 0x1400,
U8 = 0x1401,
I16 = 0x1402,
U16 = 0x1403,
I32 = 0x1404,
U32 = 0x1405,
F32 = 0x1406,
};

/**
* Conversion from `GLType` to `Type`.
*/
export const GL2TYPE = {
[GLType.I8]: Type.I8,
[GLType.U8]: Type.U8,
[GLType.I16]: Type.I16,
[GLType.U16]: Type.U16,
[GLType.I32]: Type.I32,
[GLType.I32]: Type.I32,
[GLType.U32]: Type.U32,
[GLType.F32]: Type.F32,
};

/**
* Conversion from `Type` to `GLType`.
*/
export const TYPE2GL = {
[Type.I8]: GLType.I8,
[Type.U8]: GLType.U8,
[Type.I16]: GLType.I16,
[Type.U16]: GLType.U16,
[Type.I32]: GLType.I32,
[Type.I32]: GLType.I32,
[Type.U32]: GLType.U32,
[Type.F32]: GLType.F32,
};
89 changes: 67 additions & 22 deletions packages/vector-pools/src/attrib-pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { IObjectOf, IRelease, TypedArray } from "@thi.ng/api/api";
import { assert } from "@thi.ng/api/assert";
import { align } from "@thi.ng/binary/align";
import { Pow2 } from "@thi.ng/binary/api";
import { SIZEOF, MemPoolOpts } from "@thi.ng/malloc/api";
import { SIZEOF } from "@thi.ng/malloc/api";
import { MemPool } from "@thi.ng/malloc/pool";
import { wrap } from "@thi.ng/malloc/wrap";
import { range } from "@thi.ng/transducers/iter/range";
import { ReadonlyVec, Vec } from "@thi.ng/vectors3/api";
import { AttribSpec } from "./api";
import { asNativeType } from "./convert";
import {
AttribPoolOpts,
AttribSpec,
} from "./api";

/*
* WASM mem : 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...
Expand All @@ -24,6 +28,7 @@ export class AttribPool implements
attribs: IObjectOf<TypedArray>;
order: string[];
specs: IObjectOf<AttribSpec>;
opts: AttribPoolOpts;
pool: MemPool;
addr: number;
capacity: number;
Expand All @@ -35,11 +40,16 @@ export class AttribPool implements
pool: number | ArrayBuffer | MemPool,
capacity: number,
specs: IObjectOf<AttribSpec>,
poolOpts?: MemPoolOpts
opts?: AttribPoolOpts
) {
this.opts = <AttribPoolOpts>{
resizable: true,
...opts
};
this.pool = !(pool instanceof MemPool) ?
new MemPool(pool, poolOpts) :
new MemPool(pool, this.opts.mempool) :
pool;
this.opts = opts;
this.capacity = capacity;
this.specs = {};
this.attribs = {};
Expand All @@ -49,6 +59,10 @@ export class AttribPool implements
this.initAttribs(specs, true);
}

bytes() {
return new Uint8Array(this.pool.buf, this.addr, this.capacity * this.byteStride);
}

release(releasePool = true) {
if (releasePool && this.pool) {
this.pool.release();
Expand All @@ -75,11 +89,7 @@ export class AttribPool implements
this.initDefaults(specs);
}

getAttrib(id: string) {
return this.attribs[id];
}

getAttribVal(id: string, i: number): number | Vec {
attribValue(id: string, i: number): number | Vec {
if (i >= this.capacity) return;
const spec = this.specs[id];
assert(!!spec, `invalid attrib: ${id}`);
Expand All @@ -106,7 +116,7 @@ export class AttribPool implements
}
}

setAttribVal(id: string, index: number, v: number | ReadonlyVec) {
setAttribValue(id: string, index: number, v: number | ReadonlyVec) {
const spec = this.specs[id];
assert(!!spec, `invalid attrib: ${id}`);
this.ensure(index);
Expand All @@ -122,13 +132,41 @@ export class AttribPool implements
(<Vec>v).length <= spec.size,
`wrong attrib val size, expected ${spec.size}, got ${(<Vec>v).length}`
);
buf.set(<Vec>v, index);
buf.set(<ReadonlyVec>v, index);
} else {
buf[index] = <number>v;
}
return this;
}

setAttribValues(id: string, vals: (number | ReadonlyVec)[]) {
const spec = this.specs[id];
assert(!!spec, `invalid attrib: ${id}`);
const n = vals.length;
const v = vals[0];
const stride = spec.stride;
this.ensure(n);
const buf = this.attribs[id];
const isNum = typeof v === "number";
assert(
() => (!isNum && spec.size > 1) || (isNum && spec.size === 1),
`incompatible value(s) for attrib: ${id}`
);
if (!isNum) {
assert(
(<ReadonlyVec>v).length <= spec.size,
`wrong attrib val size, expected ${spec.size}, got ${(<ReadonlyVec>v).length}`
);
for (let i = 0; i < n; i++) {
buf.set(<ReadonlyVec>vals[i], i * stride);
}
} else {
for (let i = 0; i < n; i++) {
buf[i * stride] = <number>vals[i];
}
}
}

removeAttrib(id: string) {
if (!this.attribs[id]) return false;
delete this.attribs[id];
Expand All @@ -140,14 +178,15 @@ export class AttribPool implements
}

ensure(newCapacity: number, fill = false) {
if (newCapacity < this.capacity) return;
if (newCapacity <= this.capacity) return;
assert(this.opts.resizable, `pool resizing disabled`);
// TODO add realloc()
const newAddr = this.pool.malloc(newCapacity * this.byteStride);
assert(newAddr > 0, `out of memory`);
for (let id in this.specs) {
const a = this.specs[id];
const buf = wrap(
a.type,
asNativeType(a.type),
this.pool.buf,
newAddr + (a.byteOffset || 0),
(newCapacity - 1) * a.stride + a.size
Expand All @@ -156,7 +195,6 @@ export class AttribPool implements
this.attribs[id] = buf;
}
if (fill) {
// TODO fill remainder with default values?
this.setDefaults(this.specs, this.capacity, newCapacity);
}
this.pool.free(this.addr);
Expand All @@ -169,7 +207,7 @@ export class AttribPool implements
let maxSize = inclExisting ? this.maxAttribSize : 1;
for (let id in specs) {
const a = specs[id];
const size = SIZEOF[a.type];
const size = SIZEOF[asNativeType(a.type)];
maxSize = Math.max(maxSize, size);
maxStride = Math.max(maxStride, a.byteOffset + a.size * size);
}
Expand All @@ -180,7 +218,7 @@ export class AttribPool implements
for (let id in specs) {
assert(!this.attribs[id], `attrib: ${id} already exists`);
const a = specs[id];
const size = SIZEOF[a.type];
const size = SIZEOF[asNativeType(a.type)];
const isNum = typeof a.default === "number";
assert(
() => (!isNum && a.size === (<Vec>a.default).length) || (isNum && a.size === 1),
Expand All @@ -197,14 +235,16 @@ export class AttribPool implements
}

protected updateOrder() {
this.order = Object.keys(this.specs).sort((a, b) => this.specs[a].byteOffset - this.specs[b].byteOffset);
this.order = Object.keys(this.specs).sort(
(a, b) => this.specs[a].byteOffset - this.specs[b].byteOffset
);
}

protected initDefaults(specs: IObjectOf<AttribSpec>, start = 0, end = this.capacity) {
for (let id in specs) {
const a = specs[id];
this.attribs[id] = wrap(
a.type,
asNativeType(a.type),
this.pool.buf,
this.addr + (a.byteOffset || 0),
(this.capacity - 1) * a.stride + a.size
Expand All @@ -215,8 +255,9 @@ export class AttribPool implements

protected setDefaults(specs: IObjectOf<AttribSpec>, start = 0, end = this.capacity) {
for (let id in specs) {
const buf = this.attribs[id];
const a = specs[id];
if (a.default == null) continue;
const buf = this.attribs[id];
const s = a.stride;
const v = a.default;
if (typeof v === "number") {
Expand All @@ -225,7 +266,7 @@ export class AttribPool implements
}
} else {
for (let i = start; i < end; i++) {
buf.set(<Vec>v, i * s);
buf.set(<ReadonlyVec>v, i * s);
}
}
}
Expand All @@ -237,9 +278,12 @@ export class AttribPool implements
const grow = newByteStride > this.byteStride;
let newAddr = this.addr;
if (grow) {
assert(this.opts.resizable, `pool resizing disabled`);
// TODO realloc
newAddr = this.pool.malloc(this.capacity * newByteStride);
assert(newAddr > 0, `out of memory`);
} else if (!this.opts.resizable) {
return;
}
const sameBlock = newAddr === this.addr;
const num = this.capacity - 1;
Expand All @@ -250,10 +294,11 @@ export class AttribPool implements
// create resized attrib views (in old or new address space)
for (let id in specs) {
const a = specs[id];
const dStride = newByteStride / SIZEOF[a.type];
const type = asNativeType(a.type);
const dStride = newByteStride / SIZEOF[type];
newAttribs[id] = [
wrap(
a.type,
type,
this.pool.buf,
newAddr + a.byteOffset,
num * dStride + a.size
Expand Down
23 changes: 23 additions & 0 deletions packages/vector-pools/src/convert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Type } from "@thi.ng/malloc/api";
import { GL2TYPE, GLType, TYPE2GL } from "./api";

/**
* Returns canonical `Type` value of `type` by first attempting to
* resolve it as `GLType` enum.
*
* ```
* nativeType(GLType.F32) => Type.F32
* nativeType(Type.F32) => Type.F32
* ```
*
* @param type
*/
export const asNativeType = (type: GLType | Type): Type => {
const t = GL2TYPE[type];
return t !== undefined ? t : type;
};

export const asGLType = (type: GLType | Type): GLType => {
const t = TYPE2GL[type];
return t !== undefined ? t : type;
};
2 changes: 1 addition & 1 deletion packages/vector-pools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ export * from "./alist";
export * from "./array-list";
export * from "./attrib-pool";
export * from "./linked-list";
export * from "./pool";
export * from "./vec-pool";
export * from "./wrap";
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
Type
} from "@thi.ng/malloc";
import { IVector } from "@thi.ng/vectors3/api";
import { IVecPool } from "./api";
import { GLType, IVecPool } from "./api";
import { asNativeType } from "./convert";
import { wrap } from "./wrap";

export class VecPool implements
Expand All @@ -27,12 +28,16 @@ export class VecPool implements
return this.pool.stats();
}

malloc(size: number, type: Type = Type.F32): TypedArray {
return this.pool.callocAs(type, size);
malloc(size: number, type: GLType | Type = Type.F32): TypedArray {
return this.pool.callocAs(asNativeType(type), size);
}

mallocWrapped(size: number, stride = 1, type: Type = Type.F32): IVector<any> {
const buf = this.pool.callocAs(type, size * stride);
mallocWrapped(
size: number,
stride = 1,
type: GLType | Type = Type.F32
): IVector<any> {
const buf = this.pool.callocAs(asNativeType(type), size * stride);
return wrap(buf, size, 0, stride);

}
Expand Down Expand Up @@ -61,8 +66,17 @@ export class VecPool implements
* @param estride
* @param type
*/
mallocArray(num: number, size: number, cstride = 1, estride = size, type: Type = Type.F32): IVector<any>[] {
const buf = this.malloc(Math.max(cstride, estride, size) * num, type);
mallocArray(
num: number,
size: number,
cstride = 1,
estride = size,
type: GLType | Type = Type.F32
): IVector<any>[] {
const buf = this.malloc(
Math.max(cstride, estride, size) * num,
asNativeType(type)
);
if (!buf) return;
const res: IVector<any>[] = [];
for (let i = 0; i < num; i += estride) {
Expand Down

0 comments on commit 4fe2047

Please sign in to comment.