chore(webgl): webgl Buffer inherits from API Buffer and Resource (#1526)

This commit is contained in:
Ib Green 2021-11-03 11:10:50 -07:00 committed by GitHub
parent 2b1d406d2f
commit d0bcb39f0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 317 additions and 120 deletions

View File

@ -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();
}
}
}

View 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}`;
// }
};

View 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;
}

View 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;
}
}

View 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();

View File

@ -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);

View File

@ -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 || {},

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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);
}

View File

@ -33,4 +33,8 @@ export default class Texture2D extends Texture {
Object.seal(this);
}
get [Symbol.toStringTag](): string {
return 'Texture2D';
}
}

View File

@ -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'
);

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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;
}
*/
}

View File

@ -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 {

View File

@ -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';

View File

@ -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}`);
}

View File

@ -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();
});

View File

@ -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);

View File

@ -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"