mirror of
https://github.com/visgl/luma.gl.git
synced 2026-02-01 14:33:49 +00:00
chore(webgl): webgl Buffer inherits from API Buffer and Resource (#1526)
This commit is contained in:
parent
2b1d406d2f
commit
d0bcb39f0d
@ -562,7 +562,7 @@ export default class Model {
|
||||
// Buffer is raw value (for indices) or first element of [buffer, accessor] pair
|
||||
const buffer = this.geometryBuffers[name][0] || this.geometryBuffers[name];
|
||||
if (buffer instanceof Buffer) {
|
||||
buffer.delete();
|
||||
buffer.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
30
modules/webgl/src/api/buffer.ts
Normal file
30
modules/webgl/src/api/buffer.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
|
||||
/** Abstract Buffer interface */
|
||||
export type BufferProps = ResourceProps & {
|
||||
handle?: WebGLBuffer;
|
||||
data?: any; // ArrayBufferView;
|
||||
byteLength?: number;
|
||||
target?: number;
|
||||
usage?: number;
|
||||
accessor?: any; // AccessorObject;
|
||||
|
||||
/** @deprecated */
|
||||
index?: number;
|
||||
/** @deprecated */
|
||||
offset?: number;
|
||||
/** @deprecated */
|
||||
size?: number;
|
||||
/** @deprecated */
|
||||
type?: number
|
||||
};
|
||||
|
||||
/** Abstract Buffer interface */
|
||||
export class Buffer extends Resource<BufferProps> {
|
||||
get [Symbol.toStringTag](): string {
|
||||
return 'Buffer';
|
||||
}
|
||||
// toString(): string {
|
||||
// return `Buffer${this.precision}`;
|
||||
// }
|
||||
};
|
||||
31
modules/webgl/src/api/device.ts
Normal file
31
modules/webgl/src/api/device.ts
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
import StatsManager, {lumaStats} from './stats-manager';
|
||||
|
||||
/**
|
||||
* WebGPU Device/WebGL context abstraction
|
||||
*/
|
||||
export default class Device {
|
||||
statsManager: StatsManager = lumaStats;
|
||||
|
||||
// readonly canvas: HTMLCanvasElement;
|
||||
|
||||
/** Check if this device type is supported on this platform */
|
||||
// static isSupported(): boolean;
|
||||
|
||||
/** Create device of this type (asynchronous) */
|
||||
// static async create(props?: object): Promise<Device>;
|
||||
|
||||
/** Attach to existing WebGPU device */
|
||||
// static attach(device: WebGPUDevice, props?: object): Device;
|
||||
|
||||
/** Resize the context */
|
||||
// resize(width: number, height: number): void;
|
||||
// abstract getFeatures(): string[];
|
||||
// abstract getLimits(): DeviceLimits;
|
||||
|
||||
// abstract createBuffer(props: BufferProps): Buffer;
|
||||
// abstract createTexture(props: TextureProps): Texture;
|
||||
// abstract createShader(props: ShaderProps): Shader;
|
||||
// abstract createRenderPipeline(props: RenderPipelineProps): RenderPipeline;
|
||||
// abstract createComputePipeline(props: ComputePipelineProps): ComputePipeline;
|
||||
}
|
||||
113
modules/webgl/src/api/resource.ts
Normal file
113
modules/webgl/src/api/resource.ts
Normal file
@ -0,0 +1,113 @@
|
||||
//
|
||||
import Device from './device';
|
||||
// import {uid} from '../utils';
|
||||
|
||||
export type ResourceProps = {
|
||||
id?: string;
|
||||
handle?: any;
|
||||
userData?: {[key: string]: any};
|
||||
}
|
||||
|
||||
const globalDevice = new Device();
|
||||
|
||||
/**
|
||||
* Base class for GPU (WebGPU/WebGL) Resources
|
||||
*/
|
||||
export default abstract class Resource<Props extends ResourceProps> {
|
||||
/** props.id, for debugging. */
|
||||
id: string;
|
||||
readonly props: Required<Props>;
|
||||
readonly userData: {[key: string]: any} = {};
|
||||
|
||||
// For resources that allocate GPU memory
|
||||
private readonly _device: Device;
|
||||
private allocatedBytes: number = 0;
|
||||
|
||||
/**
|
||||
* Create a new Resource. Called from Subclass
|
||||
*/
|
||||
constructor(device: Device, props: Props, defaultProps: Required<Props>) {
|
||||
this.props = this.initializeProps(props, defaultProps);
|
||||
this.id = this.props.id || 'no-id'; // TODO uid(this[Symbol.toStringTag] || this.constructor.name);
|
||||
this.userData = this.props.userData || {};
|
||||
this._device = device instanceof Device ? device : globalDevice;
|
||||
this.addStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy can be called on any resource to release it before it is garbage collected.
|
||||
*/
|
||||
destroy(): void {
|
||||
this.removeStats();
|
||||
}
|
||||
|
||||
/** @deprecated Use destroy() */
|
||||
delete(): this {
|
||||
this.destroy();
|
||||
return this;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `${this[Symbol.toStringTag] || this.constructor.name}(${this.id})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines a map of user props and default props, only including props from defaultProps
|
||||
* @returns returns a map of overridden default props
|
||||
*/
|
||||
getProps(): object {
|
||||
return this.props;
|
||||
}
|
||||
|
||||
// PROTECTED METHODS
|
||||
|
||||
/** Called by subclass to track memory allocations */
|
||||
protected trackAllocatedMemory(bytes, name = this[Symbol.toStringTag]) {
|
||||
const stats = this._device.statsManager.getStats('Resource Counts');
|
||||
stats.get('GPU Memory').addCount(bytes);
|
||||
stats.get(`${name} Memory`).addCount(bytes);
|
||||
this.allocatedBytes = bytes;
|
||||
}
|
||||
|
||||
/** Called by subclass to track memory deallocations */
|
||||
protected trackDeallocatedMemory(name = this[Symbol.toStringTag]) {
|
||||
const stats = this._device.statsManager.getStats('Resource Counts');
|
||||
stats.get('GPU Memory').subtractCount(this.allocatedBytes);
|
||||
stats.get(`${name} Memory`).subtractCount(this.allocatedBytes);
|
||||
this.allocatedBytes = 0;
|
||||
}
|
||||
|
||||
/** Called by subclass .destroy() to track object destruction */
|
||||
protected removeStats() {
|
||||
const stats = this._device.statsManager.getStats('Resource Counts');
|
||||
const name = this[Symbol.toStringTag];
|
||||
stats.get(`${name}s Active`).decrementCount();
|
||||
}
|
||||
|
||||
// PRIVATE METHODS
|
||||
|
||||
/** Called by constructor to track object creation */
|
||||
private addStats() {
|
||||
const stats = this._device.statsManager.getStats('Resource Counts');
|
||||
const name = this[Symbol.toStringTag];
|
||||
stats.get('Resources Created').incrementCount();
|
||||
stats.get(`${name}s Created`).incrementCount();
|
||||
stats.get(`${name}s Active`).incrementCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines a map of user props and default props, only including props from defaultProps
|
||||
* @param props
|
||||
* @param defaultProps
|
||||
* @returns returns a map of overridden default props
|
||||
*/
|
||||
private initializeProps(props: Props, defaultProps: Required<Props>): Required<Props> {
|
||||
const mergedProps = {...defaultProps};
|
||||
for (const key in props) {
|
||||
if (props[key] !== undefined) {
|
||||
mergedProps[key] = props[key];
|
||||
}
|
||||
}
|
||||
return mergedProps;
|
||||
}
|
||||
}
|
||||
19
modules/webgl/src/api/stats-manager.ts
Normal file
19
modules/webgl/src/api/stats-manager.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {Stats} from 'probe.gl';
|
||||
|
||||
export default class StatsManager {
|
||||
stats = new Map();
|
||||
|
||||
getStats(name: string): Stats {
|
||||
return this.get(name);
|
||||
}
|
||||
|
||||
get(name: string): Stats {
|
||||
if (!this.stats.has(name)) {
|
||||
this.stats.set(name, new Stats({id: name}));
|
||||
}
|
||||
|
||||
return this.stats.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
export const lumaStats: StatsManager = new StatsManager();
|
||||
@ -1,9 +1,9 @@
|
||||
import GL from '@luma.gl/constants';
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
import {getWebGL2Context, assertWebGL2Context, log} from '@luma.gl/gltools';
|
||||
import {Buffer, BufferProps} from '../api/buffer';
|
||||
import {AccessorObject} from '../types';
|
||||
import Accessor from './accessor';
|
||||
import {getGLTypeFromTypedArray, getTypedArrayFromGLType} from '../webgl-utils';
|
||||
import {assertWebGL2Context, log} from '@luma.gl/gltools';
|
||||
import {assert, checkProps} from '../utils';
|
||||
|
||||
const DEBUG_DATA_LENGTH = 10;
|
||||
@ -37,7 +37,9 @@ const PROP_CHECKS_SET_PROPS = {
|
||||
removedProps: DEPRECATED_PROPS
|
||||
};
|
||||
|
||||
export type BufferProps = ResourceProps & {
|
||||
/** WebGL Buffer interface */
|
||||
export type WebGLBufferProps = BufferProps & {
|
||||
handle?: WebGLBuffer;
|
||||
data?: any; // ArrayBufferView;
|
||||
byteLength?: number;
|
||||
target?: number;
|
||||
@ -54,14 +56,19 @@ export type BufferProps = ResourceProps & {
|
||||
type?: number
|
||||
}
|
||||
|
||||
export default class Buffer extends Resource {
|
||||
// readonly handle: WebGLBuffer;
|
||||
byteLength: number;
|
||||
bytesUsed: number;
|
||||
usage: number;
|
||||
accessor: Accessor;
|
||||
/** WebGL Buffer interface */
|
||||
export default class WebGLBuffer extends Buffer {
|
||||
readonly gl: WebGLRenderingContext;
|
||||
readonly gl2: WebGL2RenderingContext | null;
|
||||
readonly handle: WebGLBuffer;
|
||||
|
||||
target: number;
|
||||
usage: number;
|
||||
|
||||
byteLength: number;
|
||||
bytesUsed: number;
|
||||
|
||||
accessor: Accessor;
|
||||
|
||||
debugData;
|
||||
|
||||
@ -70,7 +77,12 @@ export default class Buffer extends Resource {
|
||||
constructor(gl: WebGLRenderingContext, byteLength: number);
|
||||
|
||||
constructor(gl: WebGLRenderingContext, props = {}) {
|
||||
super(gl, props);
|
||||
super(gl as any, props, {} as any);
|
||||
|
||||
this.gl = gl;
|
||||
this.gl2 = getWebGL2Context(gl);
|
||||
const handle = typeof props === 'object' ? (props as BufferProps).handle : undefined;
|
||||
this.handle = handle || this.gl.createBuffer();
|
||||
|
||||
// In WebGL1, need to make sure we use GL.ELEMENT_ARRAY_BUFFER when initializing element buffers
|
||||
// otherwise buffer type will lock to generic (non-element) buffer
|
||||
@ -83,6 +95,16 @@ export default class Buffer extends Resource {
|
||||
Object.seal(this);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (this.handle) {
|
||||
this.removeStats();
|
||||
this.trackDeallocatedMemory();
|
||||
this.gl.deleteBuffer(this.handle);
|
||||
// @ts-expect-error
|
||||
this.handle = null;
|
||||
}
|
||||
}
|
||||
|
||||
// returns number of elements in the buffer (assuming that the full buffer is used)
|
||||
getElementCount(accessor = this.accessor): number {
|
||||
return Math.round(this.byteLength / Accessor.getBytesPerElement(accessor));
|
||||
@ -352,7 +374,7 @@ export default class Buffer extends Resource {
|
||||
_setData(data, offset: number = 0, byteLength: number = data.byteLength + offset): this {
|
||||
assert(ArrayBuffer.isView(data));
|
||||
|
||||
this._trackDeallocatedMemory();
|
||||
this.trackDeallocatedMemory();
|
||||
|
||||
const target = this._getTarget();
|
||||
this.gl.bindBuffer(target, this.handle);
|
||||
@ -362,8 +384,8 @@ export default class Buffer extends Resource {
|
||||
|
||||
this.debugData = data.slice(0, DEBUG_DATA_LENGTH);
|
||||
this.bytesUsed = byteLength;
|
||||
|
||||
this._trackAllocatedMemory(byteLength);
|
||||
this.byteLength = byteLength;
|
||||
this.trackAllocatedMemory(byteLength);
|
||||
|
||||
// infer GL type from supplied typed array
|
||||
const type = getGLTypeFromTypedArray(data);
|
||||
@ -376,7 +398,7 @@ export default class Buffer extends Resource {
|
||||
_setByteLength(byteLength: number, usage = this.usage): this {
|
||||
assert(byteLength >= 0);
|
||||
|
||||
this._trackDeallocatedMemory();
|
||||
this.trackDeallocatedMemory();
|
||||
|
||||
// Workaround needed for Safari (#291):
|
||||
// gl.bufferData with size equal to 0 crashes. Instead create zero sized array.
|
||||
@ -394,8 +416,9 @@ export default class Buffer extends Resource {
|
||||
this.usage = usage;
|
||||
this.debugData = null;
|
||||
this.bytesUsed = byteLength;
|
||||
this.byteLength = byteLength;
|
||||
|
||||
this._trackAllocatedMemory(byteLength);
|
||||
this.trackAllocatedMemory(byteLength);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -423,16 +446,7 @@ export default class Buffer extends Resource {
|
||||
|
||||
// RESOURCE METHODS
|
||||
|
||||
_createHandle() {
|
||||
return this.gl.createBuffer();
|
||||
}
|
||||
|
||||
_deleteHandle() {
|
||||
this.gl.deleteBuffer(this.handle);
|
||||
this._trackDeallocatedMemory();
|
||||
}
|
||||
|
||||
_getParameter(pname) {
|
||||
getParameter(pname) {
|
||||
this.gl.bindBuffer(this.target, this.handle);
|
||||
const value = this.gl.getBufferParameter(this.target, pname);
|
||||
this.gl.bindBuffer(this.target, null);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import GL from '@luma.gl/constants';
|
||||
import {getWebGL2Context, assertWebGL2Context, log} from '@luma.gl/gltools';
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
import Resource, {ResourceProps} from './webgl-resource';
|
||||
import Texture2D from './texture-2d';
|
||||
import Renderbuffer from './renderbuffer';
|
||||
import {clear, clearBuffer} from './clear';
|
||||
@ -38,8 +38,8 @@ export type FramebufferProps = ImmutableFramebufferProps & {
|
||||
|
||||
type colorBufferFloatOptions = {colorBufferFloat?: boolean; colorBufferHalfFloat?: boolean};
|
||||
|
||||
export class ImmutableFramebuffer extends Resource {
|
||||
constructor(gl: WebGLRenderingContext, props?: ImmutableFramebufferProps) {
|
||||
export class ImmutableFramebuffer extends Resource<FramebufferProps> {
|
||||
constructor(gl: WebGLRenderingContext, props?: FramebufferProps) {
|
||||
super(gl, props);
|
||||
this._initialize({
|
||||
attachments: props?.attachments || {},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import GL from '@luma.gl/constants';
|
||||
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
import WebGLResource, {ResourceProps} from './webgl-resource';
|
||||
import Texture from './texture';
|
||||
import Framebuffer from './framebuffer';
|
||||
import {parseUniformName, getUniformSetter} from './uniforms';
|
||||
@ -44,7 +44,7 @@ export type ProgramDrawOptions = {
|
||||
samplers?: any;
|
||||
};
|
||||
|
||||
export default class Program extends Resource {
|
||||
export default class Program extends WebGLResource<ProgramProps> {
|
||||
configuration: ProgramConfiguration;
|
||||
// Experimental flag to avoid deleting Program object while it is cached
|
||||
_isCached = false;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// WebGL2 Query (also handles disjoint timer extensions)
|
||||
import Resource from './resource';
|
||||
import {FEATURES, hasFeatures} from '../features';
|
||||
import {isWebGL2} from '@luma.gl/gltools';
|
||||
import WebGLResource, {ResourceProps} from './webgl-resource';
|
||||
import {FEATURES, hasFeatures} from '../features';
|
||||
import {assert} from '../utils';
|
||||
|
||||
const GL_QUERY_RESULT = 0x8866; // Returns a GLuint containing the query result.
|
||||
@ -14,14 +14,13 @@ const GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = 0x8c88; // #primitives written
|
||||
const GL_ANY_SAMPLES_PASSED = 0x8c2f; // Occlusion query (if drawing passed depth test)
|
||||
const GL_ANY_SAMPLES_PASSED_CONSERVATIVE = 0x8d6a; // Occlusion query less accurate/faster version
|
||||
|
||||
export type QueryProps = {
|
||||
|
||||
}
|
||||
export type QueryProps = ResourceProps & {
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronous queries for different kinds of information
|
||||
*/
|
||||
export default class Query extends Resource {
|
||||
export default class Query extends WebGLResource<QueryProps> {
|
||||
// Returns true if Query is supported by the WebGL implementation
|
||||
// Can also check whether timestamp queries are available.
|
||||
static isSupported(gl: WebGLRenderingContext, options: string[] = []): boolean {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-inline-comments */
|
||||
import GL from '@luma.gl/constants';
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
import WebGLResource, {ResourceProps} from './webgl-resource';
|
||||
import {isRenderbufferFormatSupported, getRenderbufferFormatBytesPerPixel} from './renderbuffer-formats';
|
||||
import {isWebGL2} from '@luma.gl/gltools';
|
||||
import {assert} from '../utils';
|
||||
@ -28,24 +28,24 @@ const DEFAULT_RENDERBUFFER_PROPS = {
|
||||
* use Textures instead.
|
||||
* Renderbuffer objects also natively accommodate Multisampling (MSAA).
|
||||
*/
|
||||
export class ImmutableRenderbuffer extends Resource {
|
||||
export class ImmutableRenderbuffer extends WebGLResource<RenderbufferProps> {
|
||||
static isSupported(gl: WebGLRenderingContext, options?: {format?: number}): boolean {
|
||||
return !options.format || isRenderbufferFormatSupported(gl, options.format);
|
||||
}
|
||||
|
||||
constructor(gl: WebGLRenderingContext, props: RenderbufferProps) {
|
||||
super(gl, props);
|
||||
this._initialize(props);
|
||||
this._initialize(this.props);
|
||||
}
|
||||
|
||||
// PRIVATE METHODS
|
||||
|
||||
/** Creates and initializes a renderbuffer object's data store */
|
||||
protected _initialize(props: RenderbufferProps) {
|
||||
const {format, width, height, samples} = {...DEFAULT_RENDERBUFFER_PROPS, ...props};
|
||||
protected _initialize(props: Required<RenderbufferProps>) {
|
||||
const {format, width, height, samples} = props;
|
||||
assert(format, 'Needs format');
|
||||
|
||||
this._trackDeallocatedMemory();
|
||||
this.trackDeallocatedMemory();
|
||||
|
||||
this.gl.bindRenderbuffer(GL.RENDERBUFFER, this.handle);
|
||||
|
||||
@ -58,7 +58,7 @@ export class ImmutableRenderbuffer extends Resource {
|
||||
|
||||
this.gl.bindRenderbuffer(GL.RENDERBUFFER, null);
|
||||
|
||||
this._trackAllocatedMemory(
|
||||
this.trackAllocatedMemory(
|
||||
width * height * (samples || 1) * getRenderbufferFormatBytesPerPixel(format)
|
||||
);
|
||||
|
||||
@ -73,7 +73,7 @@ export class ImmutableRenderbuffer extends Resource {
|
||||
|
||||
_deleteHandle(): void {
|
||||
this.gl.deleteRenderbuffer(this.handle);
|
||||
this._trackDeallocatedMemory();
|
||||
this.trackDeallocatedMemory();
|
||||
}
|
||||
|
||||
_bindHandle(handle): void {
|
||||
@ -126,6 +126,7 @@ export class ImmutableRenderbuffer extends Resource {
|
||||
/** Creates and initializes a renderbuffer object's data store */
|
||||
initialize(props: RenderbufferProps): this {
|
||||
props = {...DEFAULT_RENDERBUFFER_PROPS, ...props};
|
||||
// @ts-expect-error
|
||||
this._initialize(props);
|
||||
this.format = props.format;
|
||||
this.width = props.width;
|
||||
|
||||
@ -2,26 +2,24 @@
|
||||
import GL from '@luma.gl/constants';
|
||||
import {assertWebGLContext, log} from '@luma.gl/gltools';
|
||||
import {getShaderInfo, CompilerMessage, formatCompilerLog} from '@luma.gl/shadertools';
|
||||
import WebGLResource, {ResourceProps} from './webgl-resource';
|
||||
import {parseShaderCompilerLog} from '../webgl-utils/parse-shader-compiler-log';
|
||||
import {uid, assert} from '../utils';
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
|
||||
export type ImmutableShaderProps = ResourceProps & {
|
||||
export type ShaderProps = ResourceProps & {
|
||||
source: string;
|
||||
stage?: 'vertex' | 'fragment';
|
||||
};
|
||||
|
||||
export type ShaderProps = ImmutableShaderProps & {
|
||||
/** @deprecated use props.stage */
|
||||
shaderType?: GL.VERTEX_SHADER | GL.FRAGMENT_SHADER;
|
||||
};
|
||||
|
||||
const ERR_SOURCE = 'Shader: GLSL source code must be a JavaScript string';
|
||||
|
||||
export class ImmutableShader extends Resource {
|
||||
export class ImmutableShader extends WebGLResource<ShaderProps> {
|
||||
readonly stage: 'vertex' | 'fragment';
|
||||
|
||||
constructor(gl: WebGLRenderingContext, props: ShaderProps) {
|
||||
super(gl, {id: getShaderIdFromProps(props)});
|
||||
super(gl, {id: getShaderIdFromProps(props), ...props});
|
||||
this.stage = props.stage;
|
||||
this._compile(props.source);
|
||||
}
|
||||
|
||||
@ -33,4 +33,8 @@ export default class Texture2D extends Texture {
|
||||
|
||||
Object.seal(this);
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag](): string {
|
||||
return 'Texture2D';
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ export default class Texture3D extends Texture {
|
||||
data,
|
||||
parameters = {}
|
||||
}: SetImageDataOptions) {
|
||||
this._trackDeallocatedMemory('Texture');
|
||||
this.trackDeallocatedMemory('Texture');
|
||||
|
||||
this.gl.bindTexture(this.target, this.handle);
|
||||
|
||||
@ -93,7 +93,7 @@ export default class Texture3D extends Texture {
|
||||
});
|
||||
|
||||
if (data && data.byteLength) {
|
||||
this._trackAllocatedMemory(data.byteLength, 'Texture');
|
||||
this.trackAllocatedMemory(data.byteLength, 'Texture');
|
||||
} else {
|
||||
// NOTE(Tarek): Default to RGBA bytes
|
||||
// @ts-ignore
|
||||
@ -101,7 +101,7 @@ export default class Texture3D extends Texture {
|
||||
// @ts-ignore
|
||||
const channelSize = TYPE_SIZES[this.type] || 1;
|
||||
|
||||
this._trackAllocatedMemory(
|
||||
this.trackAllocatedMemory(
|
||||
this.width * this.height * this.depth * channels * channelSize,
|
||||
'Texture'
|
||||
);
|
||||
|
||||
@ -2,7 +2,7 @@ import GL from '@luma.gl/constants';
|
||||
import {isWebGL2, assertWebGL2Context, withParameters, log} from '@luma.gl/gltools';
|
||||
import {global} from 'probe.gl/env';
|
||||
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
import WebGLResource, {ResourceProps} from './webgl-resource';
|
||||
import Buffer from './buffer';
|
||||
import {
|
||||
TEXTURE_FORMATS,
|
||||
@ -42,7 +42,7 @@ const NPOT_MIN_FILTERS = [GL.LINEAR, GL.NEAREST];
|
||||
// Note (Tarek): Do we really need to support this API?
|
||||
const WebGLBuffer = global.WebGLBuffer || function WebGLBuffer() {};
|
||||
|
||||
export default class Texture extends Resource {
|
||||
export default class Texture extends WebGLResource<TextureProps> {
|
||||
readonly MAX_ATTRIBUTES: number;
|
||||
|
||||
data;
|
||||
@ -316,7 +316,7 @@ export default class Texture extends Resource {
|
||||
*/
|
||||
/* eslint-disable max-len, max-statements, complexity */
|
||||
setImageData(options) {
|
||||
this._trackDeallocatedMemory('Texture');
|
||||
this.trackDeallocatedMemory('Texture');
|
||||
|
||||
const {
|
||||
target = this.target,
|
||||
@ -417,13 +417,13 @@ export default class Texture extends Resource {
|
||||
});
|
||||
|
||||
if (data && data.byteLength) {
|
||||
this._trackAllocatedMemory(data.byteLength, 'Texture');
|
||||
this.trackAllocatedMemory(data.byteLength, 'Texture');
|
||||
} else {
|
||||
// NOTE(Tarek): Default to RGBA bytes
|
||||
const channels = DATA_FORMAT_CHANNELS[this.dataFormat] || 4;
|
||||
const channelSize = TYPE_SIZES[this.type] || 1;
|
||||
|
||||
this._trackAllocatedMemory(this.width * this.height * channels * channelSize, 'Texture');
|
||||
this.trackAllocatedMemory(this.width * this.height * channels * channelSize, 'Texture');
|
||||
}
|
||||
|
||||
this.loaded = true;
|
||||
@ -750,7 +750,7 @@ export default class Texture extends Resource {
|
||||
|
||||
_deleteHandle(): void {
|
||||
this.gl.deleteTexture(this.handle);
|
||||
this._trackDeallocatedMemory('Texture');
|
||||
this.trackDeallocatedMemory('Texture');
|
||||
}
|
||||
|
||||
_getParameter(pname: number): any {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import GL from '@luma.gl/constants';
|
||||
import {isWebGL2, assertWebGL2Context, log} from '@luma.gl/gltools';
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
import WebGLResource, {ResourceProps} from './webgl-resource';
|
||||
import Buffer from './buffer';
|
||||
import {isObjectEmpty} from '../utils';
|
||||
|
||||
@ -8,7 +8,7 @@ export type TransformFeedbackProps = ResourceProps & {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export default class TransformFeedback extends Resource {
|
||||
export default class TransformFeedback extends WebGLResource<TransformFeedbackProps> {
|
||||
buffers = {};
|
||||
unused = {};
|
||||
configuration = null;
|
||||
|
||||
@ -2,7 +2,7 @@ import GL from '@luma.gl/constants';
|
||||
import {assertWebGL2Context, isWebGL2} from '@luma.gl/gltools';
|
||||
import {getBrowser} from 'probe.gl';
|
||||
import Program from './program';
|
||||
import Resource, {ResourceProps} from './resource';
|
||||
import WebGLResource, {ResourceProps} from './webgl-resource';
|
||||
import Buffer from './buffer';
|
||||
import {getScratchArray, fillArray} from '../utils/array-utils-flat';
|
||||
import {assert} from '../utils';
|
||||
@ -21,7 +21,7 @@ export type VertexArrayObjectProps = ResourceProps & {
|
||||
isDefaultArray?: boolean;
|
||||
};
|
||||
|
||||
export default class VertexArrayObject extends Resource {
|
||||
export default class VertexArrayObject extends WebGLResource<VertexArrayObjectProps> {
|
||||
private static MAX_ATTRIBUTES: number;
|
||||
|
||||
static isSupported(gl: WebGLRenderingContext, options?: VertexArrayObjectProps): boolean {
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
import {isWebGL2, assertWebGLContext} from '@luma.gl/gltools';
|
||||
import {lumaStats} from '../init';
|
||||
import {getKey, getKeyValue} from '../webgl-utils';
|
||||
import {uid, assert, stubRemovedMethods} from '../utils';
|
||||
import Resource from '../api/resource';
|
||||
export type {ResourceProps} from '../api/resource';
|
||||
|
||||
const ERR_RESOURCE_METHOD_UNDEFINED = 'Resource subclass must define virtual methods';
|
||||
|
||||
export type ResourceProps = {
|
||||
id?: string;
|
||||
handle?: any;
|
||||
userData?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for WebGL object wrappers
|
||||
*/
|
||||
export default class Resource {
|
||||
export default class WebGLResource<Props> extends Resource<Props> {
|
||||
id: string;
|
||||
readonly gl: WebGLRenderingContext;
|
||||
readonly gl2: WebGL2RenderingContext;
|
||||
@ -25,9 +20,13 @@ export type ResourceProps = {
|
||||
// Only meaningful for resources that allocate GPU memory
|
||||
byteLength = 0;
|
||||
|
||||
constructor(gl: WebGLRenderingContext, props?: ResourceProps) {
|
||||
constructor(gl: WebGLRenderingContext, props?: Props, defaultProps?: Required<Props>) {
|
||||
super(gl as any, props, defaultProps);
|
||||
|
||||
assertWebGLContext(gl);
|
||||
|
||||
// extends
|
||||
// @ts-expect-error
|
||||
const {id, userData = {}} = props || {};
|
||||
this.gl = gl;
|
||||
// @ts-ignore
|
||||
@ -43,14 +42,13 @@ export type ResourceProps = {
|
||||
// this.glCount = glGetContextLossCount(this.gl);
|
||||
|
||||
// Default VertexArray needs to be created with null handle, so compare against undefined
|
||||
// @ts-expect-error
|
||||
this._handle = props?.handle;
|
||||
if (this._handle === undefined) {
|
||||
this._handle = this._createHandle();
|
||||
}
|
||||
|
||||
this.byteLength = 0;
|
||||
|
||||
this._addStats();
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
@ -75,7 +73,7 @@ export type ResourceProps = {
|
||||
// @ts-ignore
|
||||
const children = this._handle && this._deleteHandle(this._handle);
|
||||
if (this._handle) {
|
||||
this._removeStats();
|
||||
this.removeStats();
|
||||
}
|
||||
this._handle = null;
|
||||
|
||||
@ -280,6 +278,7 @@ export type ResourceProps = {
|
||||
|
||||
// PRIVATE METHODS
|
||||
|
||||
/*
|
||||
_addStats() {
|
||||
const name = this.constructor.name;
|
||||
const stats = lumaStats.get('Resource Counts');
|
||||
@ -296,7 +295,7 @@ export type ResourceProps = {
|
||||
stats.get(`${name}s Active`).decrementCount();
|
||||
}
|
||||
|
||||
_trackAllocatedMemory(bytes, name = this.constructor.name) {
|
||||
trackAllocatedMemory(bytes, name = this.constructor.name) {
|
||||
const stats = lumaStats.get('Memory Usage');
|
||||
|
||||
stats.get('GPU Memory').addCount(bytes);
|
||||
@ -304,11 +303,12 @@ export type ResourceProps = {
|
||||
this.byteLength = bytes;
|
||||
}
|
||||
|
||||
_trackDeallocatedMemory(name = this.constructor.name) {
|
||||
trackDeallocatedMemory(name = this.constructor.name) {
|
||||
const stats = lumaStats.get('Memory Usage');
|
||||
|
||||
stats.get('GPU Memory').subtractCount(this.byteLength);
|
||||
stats.get(`${name} Memory`).subtractCount(this.byteLength);
|
||||
this.byteLength = 0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
//
|
||||
import type {CreateGLContextOptions} from '@luma.gl/gltools';
|
||||
import {createGLContext, isWebGL2} from '@luma.gl/gltools';
|
||||
import Buffer, {BufferProps} from '../classes/buffer';
|
||||
import WebGLBuffer, {WebGLBufferProps} from '../classes/buffer';
|
||||
import Texture2D, {Texture2DProps} from '../classes/texture-2d';
|
||||
|
||||
export default class GLContext {
|
||||
@ -13,8 +13,8 @@ export default class GLContext {
|
||||
this.gl2 = isWebGL2(this.gl) ? this.gl as WebGL2RenderingContext : null;
|
||||
}
|
||||
|
||||
createBuffer(props: BufferProps): Buffer {
|
||||
return new Buffer(this.gl, props);
|
||||
createBuffer(props: WebGLBufferProps): WebGLBuffer {
|
||||
return new WebGLBuffer(this.gl, props);
|
||||
}
|
||||
|
||||
createTexture(props: Texture2DProps): Texture2D {
|
||||
|
||||
@ -22,7 +22,8 @@ export {default as canCompileGLGSExtension} from './features/check-glsl-extensio
|
||||
export {default as Accessor} from './classes/accessor';
|
||||
|
||||
// WebGL1 classes
|
||||
export {default as Buffer, BufferProps} from './classes/buffer';
|
||||
export type {WebGLBufferProps as BufferProps} from './classes/buffer';
|
||||
export {default as Buffer} from './classes/buffer';
|
||||
export {default as Texture2D, Texture2DProps} from './classes/texture-2d';
|
||||
export {default as TextureCube, TextureCubeProps} from './classes/texture-cube';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {log} from '@luma.gl/gltools';
|
||||
import {Stats} from 'probe.gl';
|
||||
import {isBrowser, global} from 'probe.gl/env';
|
||||
import {lumaStats} from './api/stats-manager';
|
||||
|
||||
// Version detection using babel plugin
|
||||
// @ts-ignore
|
||||
@ -11,20 +11,6 @@ const STARTUP_MESSAGE = 'set luma.log.level=1 (or higher) to trace rendering';
|
||||
// 0: none, 1: minimal, 2: verbose, 3: attribute/uniforms, 4: gl logs
|
||||
// luma.log.break[], set to gl funcs, luma.log.profile[] set to model names`;
|
||||
|
||||
class StatsManager {
|
||||
stats = new Map();
|
||||
|
||||
get(name: string): Stats {
|
||||
if (!this.stats.has(name)) {
|
||||
this.stats.set(name, new Stats({id: name}));
|
||||
}
|
||||
|
||||
return this.stats.get(name);
|
||||
}
|
||||
}
|
||||
|
||||
const lumaStats: StatsManager = new StatsManager();
|
||||
|
||||
if (global.luma && global.luma.VERSION !== VERSION) {
|
||||
throw new Error(`luma.gl - multiple VERSIONs detected: ${global.luma.VERSION} vs ${VERSION}`);
|
||||
}
|
||||
|
||||
@ -10,15 +10,15 @@ test('Buffer#constructor/delete', (t) => {
|
||||
t.ok(isWebGL(gl), 'Created gl context');
|
||||
|
||||
// @ts-ignore
|
||||
t.throws(() => new Buffer(), /.*WebGLRenderingContext.*/, 'Buffer throws on missing gl context');
|
||||
t.throws(() => new Buffer(), 'Buffer throws on missing gl context');
|
||||
|
||||
const buffer = new Buffer(gl, {target: GL.ARRAY_BUFFER});
|
||||
t.ok(buffer instanceof Buffer, 'Buffer construction successful');
|
||||
|
||||
buffer.delete();
|
||||
buffer.destroy();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer delete successful');
|
||||
|
||||
buffer.delete();
|
||||
buffer.destroy();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer repeated delete successful');
|
||||
|
||||
t.end();
|
||||
@ -68,8 +68,9 @@ test('Buffer#constructor offset and size', (t) => {
|
||||
test('Buffer#bind/unbind', (t) => {
|
||||
const {gl} = fixture;
|
||||
|
||||
const buffer = new Buffer(gl, {target: GL.ARRAY_BUFFER}).bind().unbind().delete();
|
||||
const buffer = new Buffer(gl, {target: GL.ARRAY_BUFFER}).bind().unbind();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer bind/unbind successful');
|
||||
buffer.destroy();
|
||||
|
||||
t.end();
|
||||
});
|
||||
@ -82,8 +83,9 @@ test('Buffer#bind/unbind with index', (t) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const buffer = new Buffer(gl2, {target: GL.UNIFORM_BUFFER, index: 0}).bind().unbind().delete();
|
||||
const buffer = new Buffer(gl2, {target: GL.UNIFORM_BUFFER, index: 0}).bind().unbind();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer bind/unbind with index successful');
|
||||
buffer.destroy();
|
||||
|
||||
t.end();
|
||||
});
|
||||
@ -95,9 +97,9 @@ test('Buffer#construction', (t) => {
|
||||
|
||||
buffer = new Buffer(gl, {target: GL.ARRAY_BUFFER, data: new Float32Array([1, 2, 3])})
|
||||
.bind()
|
||||
.unbind()
|
||||
.delete();
|
||||
.unbind();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer(ARRAY_BUFFER) successful');
|
||||
buffer.destroy();
|
||||
|
||||
// TODO - buffer could check for integer ELEMENT_ARRAY_BUFFER types
|
||||
buffer = new Buffer(gl, {
|
||||
@ -105,9 +107,9 @@ test('Buffer#construction', (t) => {
|
||||
data: new Float32Array([1, 2, 3])
|
||||
})
|
||||
.bind()
|
||||
.unbind()
|
||||
.delete();
|
||||
.unbind();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer(ELEMENT_ARRAY_BUFFER) successful');
|
||||
buffer.destroy();
|
||||
|
||||
t.end();
|
||||
});
|
||||
@ -120,31 +122,31 @@ test('Buffer#initialize/subData', (t) => {
|
||||
buffer = new Buffer(gl, {target: GL.ARRAY_BUFFER})
|
||||
.initialize({data: new Float32Array([1, 2, 3])})
|
||||
.bind()
|
||||
.unbind()
|
||||
.delete();
|
||||
.unbind();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer.subData(ARRAY_BUFFER) successful');
|
||||
buffer.destroy();
|
||||
|
||||
buffer = new Buffer(gl, {target: GL.ARRAY_BUFFER, data: new Float32Array([1, 2, 3])})
|
||||
.initialize({data: new Float32Array([1, 2, 3])})
|
||||
.bind()
|
||||
.unbind()
|
||||
.delete();
|
||||
.unbind();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer.subData(ARRAY_BUFFER) successful');
|
||||
buffer.destroy();
|
||||
|
||||
buffer = new Buffer(gl, {target: GL.ELEMENT_ARRAY_BUFFER})
|
||||
.initialize({data: new Float32Array([1, 2, 3])})
|
||||
.bind()
|
||||
.unbind()
|
||||
.delete();
|
||||
.unbind();
|
||||
t.ok(buffer instanceof Buffer, 'buffer.initialize(ELEMENT_ARRAY_BUFFER) successful');
|
||||
buffer.destroy();
|
||||
|
||||
buffer = new Buffer(gl, {target: GL.ELEMENT_ARRAY_BUFFER})
|
||||
.initialize({data: new Float32Array([1, 2, 3])})
|
||||
.subData({data: new Float32Array([1, 1, 1])})
|
||||
.bind()
|
||||
.unbind()
|
||||
.delete();
|
||||
.unbind();
|
||||
t.ok(buffer instanceof Buffer, 'Buffer.subData(ARRAY_ELEMENT_BUFFER) successful');
|
||||
buffer.destroy();
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
@ -42,8 +42,7 @@ test('WebGL#TransformFeedback constructor/delete', (t) => {
|
||||
t.throws(
|
||||
// @ts-ignore
|
||||
() => new TransformFeedback(),
|
||||
/.*Invalid WebGLRenderingContext.*/,
|
||||
'Buffer throws on missing gl context'
|
||||
'TransformFeedback throws on missing gl context'
|
||||
);
|
||||
|
||||
const tf = new TransformFeedback(gl2);
|
||||
|
||||
@ -3805,9 +3805,9 @@ camelcase@^5.0.0, camelcase@^5.3.1:
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001254:
|
||||
version "1.0.30001274"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001274.tgz"
|
||||
integrity sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==
|
||||
version "1.0.30001275"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001275.tgz"
|
||||
integrity sha512-ihJVvj8RX0kn9GgP43HKhb5q9s2XQn4nEQhdldEJvZhCsuiB2XOq6fAMYQZaN6FPWfsr2qU0cdL0CSbETwbJAg==
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user