扩展typedarray存储系统,允许自动类型推断@AutoTyped

This commit is contained in:
YHH
2025-09-30 11:00:05 +08:00
parent d0cb7d5359
commit aa33cad4fa
6 changed files with 801 additions and 24 deletions

View File

@@ -1,12 +1,11 @@
import { Component } from '../Component';
import { BitMask64Utils, BitMask64Data } from '../Utils/BigIntCompatibility';
import { SoAStorage, EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from './SoAStorage';
import { SoAStorage, SupportedTypedArray } from './SoAStorage';
import { createLogger } from '../../Utils/Logger';
import { getComponentTypeName } from '../Decorators';
import { ComponentRegistry, ComponentType } from './ComponentStorage/ComponentRegistry';
// 重新导出装饰器和核心类型
export { EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy };
// 导出核心类型
export { ComponentRegistry, ComponentType };
@@ -203,9 +202,9 @@ export class ComponentStorageManager {
* @returns TypedArray或null
*/
public getFieldArray<T extends Component>(
componentType: ComponentType<T>,
componentType: ComponentType<T>,
fieldName: string
): Float32Array | Float64Array | Int32Array | null {
): SupportedTypedArray | null {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getFieldArray(fieldName) : null;
}
@@ -217,9 +216,9 @@ export class ComponentStorageManager {
* @returns TypedArray或null
*/
public getTypedFieldArray<T extends Component, K extends keyof T>(
componentType: ComponentType<T>,
componentType: ComponentType<T>,
fieldName: K
): Float32Array | Float64Array | Int32Array | null {
): SupportedTypedArray | null {
const soaStorage = this.getSoAStorage(componentType);
return soaStorage ? soaStorage.getTypedFieldArray(fieldName) : null;
}

View File

@@ -60,6 +60,78 @@ export function Int32(target: any, propertyKey: string | symbol): void {
target.constructor.__int32Fields.add(key);
}
/**
* 32位无符号整数装饰器
* 标记字段使用Uint32Array存储适用于无符号整数如ID、标志位等
*/
export function Uint32(target: any, propertyKey: string | symbol): void {
const key = String(propertyKey);
if (!target.constructor.__uint32Fields) {
target.constructor.__uint32Fields = new Set();
}
target.constructor.__uint32Fields.add(key);
}
/**
* 16位整数装饰器
* 标记字段使用Int16Array存储适用于小范围整数
*/
export function Int16(target: any, propertyKey: string | symbol): void {
const key = String(propertyKey);
if (!target.constructor.__int16Fields) {
target.constructor.__int16Fields = new Set();
}
target.constructor.__int16Fields.add(key);
}
/**
* 16位无符号整数装饰器
* 标记字段使用Uint16Array存储适用于小范围无符号整数
*/
export function Uint16(target: any, propertyKey: string | symbol): void {
const key = String(propertyKey);
if (!target.constructor.__uint16Fields) {
target.constructor.__uint16Fields = new Set();
}
target.constructor.__uint16Fields.add(key);
}
/**
* 8位整数装饰器
* 标记字段使用Int8Array存储适用于很小的整数值
*/
export function Int8(target: any, propertyKey: string | symbol): void {
const key = String(propertyKey);
if (!target.constructor.__int8Fields) {
target.constructor.__int8Fields = new Set();
}
target.constructor.__int8Fields.add(key);
}
/**
* 8位无符号整数装饰器
* 标记字段使用Uint8Array存储适用于字节值、布尔标志等
*/
export function Uint8(target: any, propertyKey: string | symbol): void {
const key = String(propertyKey);
if (!target.constructor.__uint8Fields) {
target.constructor.__uint8Fields = new Set();
}
target.constructor.__uint8Fields.add(key);
}
/**
* 8位夹紧整数装饰器
* 标记字段使用Uint8ClampedArray存储适用于颜色值等需要夹紧的数据
*/
export function Uint8Clamped(target: any, propertyKey: string | symbol): void {
const key = String(propertyKey);
if (!target.constructor.__uint8ClampedFields) {
target.constructor.__uint8ClampedFields = new Set();
}
target.constructor.__uint8ClampedFields.add(key);
}
/**
* 序列化Map装饰器
@@ -109,13 +181,161 @@ export function DeepCopy(target: any, propertyKey: string | symbol): void {
target.constructor.__deepCopyFields.add(key);
}
/**
* 自动类型推断装饰器
* 根据字段的默认值和数值范围自动选择最优的TypedArray类型
*
* @param options 类型推断选项
* @param options.minValue 数值的最小值(用于范围优化)
* @param options.maxValue 数值的最大值(用于范围优化)
* @param options.precision 是否需要浮点精度true: 使用浮点数组, false: 使用整数数组)
* @param options.signed 是否需要符号位(仅在整数模式下有效)
*/
export function AutoTyped(options?: {
minValue?: number;
maxValue?: number;
precision?: boolean;
signed?: boolean;
}) {
return function (target: any, propertyKey: string | symbol): void {
const key = String(propertyKey);
if (!target.constructor.__autoTypedFields) {
target.constructor.__autoTypedFields = new Map();
}
target.constructor.__autoTypedFields.set(key, options || {});
};
}
/**
* 自动类型推断器
* 根据数值类型和范围自动选择最优的TypedArray类型
*/
export class TypeInference {
/**
* 根据数值范围推断最优的TypedArray类型
*/
public static inferOptimalType(value: any, options: {
minValue?: number;
maxValue?: number;
precision?: boolean;
signed?: boolean;
} = {}): string {
const type = typeof value;
if (type === 'boolean') {
return 'uint8'; // 布尔值使用最小的无符号整数
}
if (type !== 'number') {
return 'float32'; // 非数值类型默认使用Float32
}
const { minValue, maxValue, precision, signed } = options;
// 如果显式要求精度,使用浮点数
if (precision === true) {
// 检查是否需要双精度
if (Math.abs(value) > 3.4028235e+38 || (minValue !== undefined && Math.abs(minValue) > 3.4028235e+38) || (maxValue !== undefined && Math.abs(maxValue) > 3.4028235e+38)) {
return 'float64';
}
return 'float32';
}
// 如果显式禁用精度,或者是整数值,尝试使用整数数组
if (precision === false || Number.isInteger(value)) {
const actualMin = minValue !== undefined ? minValue : value;
const actualMax = maxValue !== undefined ? maxValue : value;
const needsSigned = signed !== false && (actualMin < 0 || value < 0);
// 根据范围选择最小的整数类型
if (needsSigned) {
// 有符号整数
if (actualMin >= -128 && actualMax <= 127) {
return 'int8';
} else if (actualMin >= -32768 && actualMax <= 32767) {
return 'int16';
} else if (actualMin >= -2147483648 && actualMax <= 2147483647) {
return 'int32';
} else {
return 'float64'; // 超出int32范围使用双精度浮点
}
} else {
// 无符号整数
if (actualMax <= 255) {
return 'uint8';
} else if (actualMax <= 65535) {
return 'uint16';
} else if (actualMax <= 4294967295) {
return 'uint32';
} else {
return 'float64'; // 超出uint32范围使用双精度浮点
}
}
}
// 默认情况:检查是否为小数
if (!Number.isInteger(value)) {
return 'float32';
}
// 整数值,但没有指定范围,根据值的大小选择
if (value >= 0 && value <= 255) {
return 'uint8';
} else if (value >= -128 && value <= 127) {
return 'int8';
} else if (value >= 0 && value <= 65535) {
return 'uint16';
} else if (value >= -32768 && value <= 32767) {
return 'int16';
} else if (value >= 0 && value <= 4294967295) {
return 'uint32';
} else if (value >= -2147483648 && value <= 2147483647) {
return 'int32';
} else {
return 'float64';
}
}
/**
* 根据推断的类型名创建对应的TypedArray构造函数
*/
public static getTypedArrayConstructor(typeName: string): typeof Float32Array | typeof Float64Array | typeof Int32Array | typeof Uint32Array | typeof Int16Array | typeof Uint16Array | typeof Int8Array | typeof Uint8Array | typeof Uint8ClampedArray {
switch (typeName) {
case 'float32': return Float32Array;
case 'float64': return Float64Array;
case 'int32': return Int32Array;
case 'uint32': return Uint32Array;
case 'int16': return Int16Array;
case 'uint16': return Uint16Array;
case 'int8': return Int8Array;
case 'uint8': return Uint8Array;
case 'uint8clamped': return Uint8ClampedArray;
default: return Float32Array;
}
}
}
/**
* SoA存储器支持的TypedArray类型
*/
export type SupportedTypedArray =
| Float32Array
| Float64Array
| Int32Array
| Uint32Array
| Int16Array
| Uint16Array
| Int8Array
| Uint8Array
| Uint8ClampedArray;
/**
* SoA存储器需要装饰器启用
* 使用Structure of Arrays存储模式在大规模批量操作时提供优异性能
*/
export class SoAStorage<T extends Component> {
private static readonly _logger = createLogger('SoAStorage');
private fields = new Map<string, Float32Array | Float64Array | Int32Array>();
private fields = new Map<string, SupportedTypedArray>();
private stringFields = new Map<string, string[]>(); // 专门存储字符串
private serializedFields = new Map<string, string[]>(); // 序列化存储Map/Set/Array
private complexFields = new Map<number, Map<string, any>>(); // 存储复杂对象
@@ -137,6 +357,13 @@ export class SoAStorage<T extends Component> {
const float64Fields = (componentType as any).__float64Fields || new Set();
const float32Fields = (componentType as any).__float32Fields || new Set();
const int32Fields = (componentType as any).__int32Fields || new Set();
const uint32Fields = (componentType as any).__uint32Fields || new Set();
const int16Fields = (componentType as any).__int16Fields || new Set();
const uint16Fields = (componentType as any).__uint16Fields || new Set();
const int8Fields = (componentType as any).__int8Fields || new Set();
const uint8Fields = (componentType as any).__uint8Fields || new Set();
const uint8ClampedFields = (componentType as any).__uint8ClampedFields || new Set();
const autoTypedFields = (componentType as any).__autoTypedFields || new Map();
const serializeMapFields = (componentType as any).__serializeMapFields || new Set();
const serializeSetFields = (componentType as any).__serializeSetFields || new Set();
const serializeArrayFields = (componentType as any).__serializeArrayFields || new Set();
@@ -151,22 +378,52 @@ export class SoAStorage<T extends Component> {
if (highPrecisionFields.has(key)) {
// 标记为高精度,作为复杂对象处理
// 不添加到fields会在updateComponentAtIndex中自动添加到complexFields
} else if (autoTypedFields.has(key)) {
// 使用自动类型推断
const options = autoTypedFields.get(key);
const inferredType = TypeInference.inferOptimalType(value, options);
const ArrayConstructor = TypeInference.getTypedArrayConstructor(inferredType);
this.fields.set(key, new ArrayConstructor(this._capacity));
SoAStorage._logger.info(`字段 ${key} 自动推断为 ${inferredType} 类型,值: ${value}, 选项:`, options);
} else if (float64Fields.has(key)) {
// 使用Float64Array存储
// 使用Float64Array存储(高精度浮点数)
this.fields.set(key, new Float64Array(this._capacity));
} else if (int32Fields.has(key)) {
// 使用Int32Array存储
// 使用Int32Array存储32位有符号整数
this.fields.set(key, new Int32Array(this._capacity));
} else if (uint32Fields.has(key)) {
// 使用Uint32Array存储32位无符号整数
this.fields.set(key, new Uint32Array(this._capacity));
} else if (int16Fields.has(key)) {
// 使用Int16Array存储16位有符号整数
this.fields.set(key, new Int16Array(this._capacity));
} else if (uint16Fields.has(key)) {
// 使用Uint16Array存储16位无符号整数
this.fields.set(key, new Uint16Array(this._capacity));
} else if (int8Fields.has(key)) {
// 使用Int8Array存储8位有符号整数
this.fields.set(key, new Int8Array(this._capacity));
} else if (uint8Fields.has(key)) {
// 使用Uint8Array存储8位无符号整数
this.fields.set(key, new Uint8Array(this._capacity));
} else if (uint8ClampedFields.has(key)) {
// 使用Uint8ClampedArray存储8位夹紧无符号整数
this.fields.set(key, new Uint8ClampedArray(this._capacity));
} else if (float32Fields.has(key)) {
// 使用Float32Array存储
// 使用Float32Array存储32位浮点数
this.fields.set(key, new Float32Array(this._capacity));
} else {
// 默认使用Float32Array
this.fields.set(key, new Float32Array(this._capacity));
}
} else if (type === 'boolean') {
// 布尔值使用Float32Array存储为0/1
this.fields.set(key, new Float32Array(this._capacity));
// 布尔值默认使用Uint8Array存储为0/1(更节省内存)
if (uint8Fields.has(key) || (!float32Fields.has(key) && !float64Fields.has(key))) {
this.fields.set(key, new Uint8Array(this._capacity));
} else {
// 兼容性:如果显式指定浮点类型则使用原有方式
this.fields.set(key, new Float32Array(this._capacity));
}
} else if (type === 'string') {
// 字符串专门处理
this.stringFields.set(key, new Array(this._capacity));
@@ -430,16 +687,32 @@ export class SoAStorage<T extends Component> {
private resize(newCapacity: number): void {
// 调整数值字段的TypedArray
for (const [fieldName, oldArray] of this.fields.entries()) {
let newArray: Float32Array | Float64Array | Int32Array;
let newArray: SupportedTypedArray;
if (oldArray instanceof Float32Array) {
newArray = new Float32Array(newCapacity);
} else if (oldArray instanceof Float64Array) {
newArray = new Float64Array(newCapacity);
} else {
} else if (oldArray instanceof Int32Array) {
newArray = new Int32Array(newCapacity);
} else if (oldArray instanceof Uint32Array) {
newArray = new Uint32Array(newCapacity);
} else if (oldArray instanceof Int16Array) {
newArray = new Int16Array(newCapacity);
} else if (oldArray instanceof Uint16Array) {
newArray = new Uint16Array(newCapacity);
} else if (oldArray instanceof Int8Array) {
newArray = new Int8Array(newCapacity);
} else if (oldArray instanceof Uint8Array) {
newArray = new Uint8Array(newCapacity);
} else if (oldArray instanceof Uint8ClampedArray) {
newArray = new Uint8ClampedArray(newCapacity);
} else {
// 默认回退到Float32Array
newArray = new Float32Array(newCapacity);
SoAStorage._logger.warn(`未知的TypedArray类型用于字段 ${fieldName}回退到Float32Array`);
}
newArray.set(oldArray);
this.fields.set(fieldName, newArray);
}
@@ -469,11 +742,11 @@ export class SoAStorage<T extends Component> {
return Array.from(this.entityToIndex.values());
}
public getFieldArray(fieldName: string): Float32Array | Float64Array | Int32Array | null {
public getFieldArray(fieldName: string): SupportedTypedArray | null {
return this.fields.get(fieldName) || null;
}
public getTypedFieldArray<K extends keyof T>(fieldName: K): Float32Array | Float64Array | Int32Array | null {
public getTypedFieldArray<K extends keyof T>(fieldName: K): SupportedTypedArray | null {
return this.fields.get(String(fieldName)) || null;
}
@@ -566,16 +839,38 @@ export class SoAStorage<T extends Component> {
for (const [fieldName, array] of this.fields.entries()) {
let bytesPerElement: number;
let typeName: string;
if (array instanceof Float32Array) {
bytesPerElement = 4;
typeName = 'float32';
} else if (array instanceof Float64Array) {
bytesPerElement = 8;
typeName = 'float64';
} else {
} else if (array instanceof Int32Array) {
bytesPerElement = 4;
typeName = 'int32';
} else if (array instanceof Uint32Array) {
bytesPerElement = 4;
typeName = 'uint32';
} else if (array instanceof Int16Array) {
bytesPerElement = 2;
typeName = 'int16';
} else if (array instanceof Uint16Array) {
bytesPerElement = 2;
typeName = 'uint16';
} else if (array instanceof Int8Array) {
bytesPerElement = 1;
typeName = 'int8';
} else if (array instanceof Uint8Array) {
bytesPerElement = 1;
typeName = 'uint8';
} else if (array instanceof Uint8ClampedArray) {
bytesPerElement = 1;
typeName = 'uint8clamped';
} else {
// 默认回退
bytesPerElement = 4;
typeName = 'unknown';
}
const memory = array.length * bytesPerElement;
@@ -603,7 +898,7 @@ export class SoAStorage<T extends Component> {
* 执行向量化批量操作
* @param operation 操作函数,接收字段数组和活跃索引
*/
public performVectorizedOperation(operation: (fieldArrays: Map<string, Float32Array | Float64Array | Int32Array>, activeIndices: number[]) => void): void {
public performVectorizedOperation(operation: (fieldArrays: Map<string, SupportedTypedArray>, activeIndices: number[]) => void): void {
const activeIndices = this.getActiveIndices();
operation(this.fields, activeIndices);
}

View File

@@ -0,0 +1,49 @@
/**
* 统一的存储装饰器导出文件
*
* 用户可以从这里导入所有的SoA存储装饰器而不需要知道内部实现细节
*
* @example
* ```typescript
* import { EnableSoA, Float32, Int16, Uint8 } from './ECS/Core/StorageDecorators';
*
* @EnableSoA
* class TransformComponent extends Component {
* @Float32 x: number = 0;
* @Float32 y: number = 0;
* @Int16 layer: number = 0;
* @Uint8 visible: boolean = true;
* }
* ```
*/
// 从SoAStorage导入所有装饰器和类型
export {
// 启用装饰器
EnableSoA,
// 数值类型装饰器
HighPrecision,
Float64,
Float32,
Int32,
Uint32,
Int16,
Uint16,
Int8,
Uint8,
Uint8Clamped,
// 自动类型推断
AutoTyped,
TypeInference,
// 序列化装饰器
SerializeMap,
SerializeSet,
SerializeArray,
DeepCopy,
// 类型定义
SupportedTypedArray
} from './SoAStorage';

View File

@@ -10,4 +10,5 @@ export { World, IWorldConfig } from './World';
export { WorldManager, IWorldManagerConfig } from './WorldManager';
export * from './Core/Events';
export * from './Core/Query';
export * from './Core/Storage';
export * from './Core/Storage';
export * from './Core/StorageDecorators';