UNPKG

@itwin/core-frontend

Version:
762 lines • 35.5 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module WebGL */ import { assert, BentleyStatus, Dictionary, dispose, Id64 } from "@itwin/core-bentley"; import { ColorDef, Gradient, ImageBuffer, ImageBufferFormat, ImageSourceFormat, IModelError, RenderMaterialParams, RenderTexture, TextureMapping, TextureTransparency } from "@itwin/core-common"; import { Range3d, Transform } from "@itwin/core-geometry"; import { Capabilities } from "@itwin/webgl-compatibility"; import { IModelApp } from "../../../IModelApp"; import { IModelConnection } from "../../../IModelConnection"; import { imageElementFromImageSource } from "../../../common/ImageUtil"; import { GraphicBranch } from "../../../render/GraphicBranch"; import { PrimitiveBuilder } from "../../../internal/render/PrimitiveBuilder"; import { RenderSystem, } from "../../../render/RenderSystem"; import { RenderDiagnostics } from "../RenderSystemDebugControl"; import { BackgroundMapDrape } from "./BackgroundMapDrape"; import { SkyBoxQuadsGeometry, SkySphereViewportQuadGeometry } from "./CachedGeometry"; import { ClipVolume } from "./ClipVolume"; import { Debug } from "./Diagnostics"; import { FrameBufferStack } from "./FrameBuffer"; import { GL } from "./GL"; import { GLTimer } from "./GLTimer"; import { AnimationTransformBranch, Batch, Branch, GraphicOwner, GraphicsArray } from "./Graphic"; import { InstanceBuffers, isInstancedGraphicParams, PatternBuffers, RenderInstancesImpl } from "./InstancedGeometry"; import { Layer, LayerContainer } from "./Layer"; import { LineCode } from "./LineCode"; import { Material } from "./Material"; import { MeshGraphic, MeshRenderGeometry } from "./Mesh"; import { PlanarGridGeometry } from "./PlanarGrid"; import { PointCloudGeometry } from "./PointCloud"; import { PointStringGeometry } from "./PointString"; import { PolylineGeometry } from "./Polyline"; import { Primitive, SkyCubePrimitive, SkySpherePrimitive } from "./Primitive"; import { RealityMeshGeometry } from "./RealityMesh"; import { RenderBufferMultiSample } from "./RenderBuffer"; import { TextureUnit } from "./RenderFlags"; import { RenderState } from "./RenderState"; import { createScreenSpaceEffectBuilder, ScreenSpaceEffects } from "./ScreenSpaceEffect"; import { OffScreenTarget, OnScreenTarget } from "./Target"; import { Techniques } from "./Technique"; import { ExternalTextureLoader, Texture, TextureHandle } from "./Texture"; import { _batch, _branch, _featureTable, _nodes } from "../../../common/internal/Symbols"; /** Id map holds key value pairs for both materials and textures, useful for caching such objects. * @internal */ export class IdMap { _iModel; /** Mapping of materials by their key values. */ materials = new Map(); /** Mapping of textures by their key values. */ textures = new Map(); /** Mapping of textures using gradient symbology. */ gradients = new Dictionary((lhs, rhs) => Gradient.Symb.compareSymb(lhs, rhs)); /** Pending promises to create a texture from an ImageSource. This prevents us from decoding the same ImageSource multiple times */ texturesFromImageSources = new Map(); constructor(iModel) { this._iModel = iModel; } get isDisposed() { return 0 === this.textures.size && 0 === this.gradients.size; } [Symbol.dispose]() { const textureArr = Array.from(this.textures.values()); const gradientArr = this.gradients.extractArrays().values; for (const texture of textureArr) dispose(texture); for (const gradient of gradientArr) dispose(gradient); this.textures.clear(); this.gradients.clear(); this.materials.clear(); } /** Add a material to this IdMap, given that it has a valid key. */ addMaterial(material) { if (material.key) this.materials.set(material.key, material); } /** Add a texture to this IdMap, given that it has a valid string key. If specified, it will instead use the key parameter, which could also be a gradient symb. */ addTexture(texture, key) { assert(texture instanceof Texture); if (undefined !== key) { if ("string" === typeof key) this.textures.set(key, texture); else this.addGradient(key, texture); } else if (texture.key) this.textures.set(texture.key, texture); } /** Add a texture to this IdMap using gradient symbology. */ addGradient(gradientSymb, texture) { this.gradients.set(gradientSymb, texture); } /** Find a cached material using its key. If not found, returns undefined. */ findMaterial(key) { return this.materials.get(key); } /** Find a cached gradient using the gradient symbology. If not found, returns undefined. */ findGradient(symb) { return this.gradients.get(symb); } /** Find or create a new material given material parameters. This will cache the material if its key is valid. */ getMaterial(params) { if (!params.key || !Id64.isValidId64(params.key)) // Only cache persistent materials. return new Material(params); let material = this.materials.get(params.key); if (!material) { material = new Material(params); this.materials.set(params.key, material); } return material; } findTexture(key) { if (undefined === key) return undefined; else if (typeof key === "string") return this.textures.get(key); else return this.findGradient(key); } getTextureFromElement(key, iModel, params, format) { let tex = this.findTexture(params.key); if (tex) return tex; const handle = TextureHandle.createForElement(key, iModel, params.type, format, (_, data) => { if (tex) { assert(tex instanceof Texture); tex.transparency = data.transparency ?? TextureTransparency.Mixed; } }); if (!handle) return undefined; tex = new Texture({ handle, type: params.type, ownership: { key, iModel }, transparency: TextureTransparency.Opaque }); this.addTexture(tex); return tex; } async getTextureFromImageSource(args, key) { const texture = this.findTexture(key); if (texture) return texture; // Are we already in the process of decoding this image? let promise = this.texturesFromImageSources.get(key); if (promise) return promise; promise = this.createTextureFromImageSource(args, key); this.texturesFromImageSources.set(key, promise); return promise; } async createTextureFromImageSource(args, key) { // JPEGs don't support transparency. const transparency = ImageSourceFormat.Jpeg === args.source.format ? TextureTransparency.Opaque : (args.transparency ?? TextureTransparency.Mixed); try { const image = await imageElementFromImageSource(args.source); if (!IModelApp.hasRenderSystem) return undefined; return IModelApp.renderSystem.createTexture({ type: args.type, ownership: args.ownership, image: { source: image, transparency, }, }); } catch { return undefined; } finally { this.texturesFromImageSources.delete(key); } } getTextureFromCubeImages(posX, negX, posY, negY, posZ, negZ, params) { let tex = this.findTexture(params.key); if (tex) return tex; const handle = TextureHandle.createForCubeImages(posX, negX, posY, negY, posZ, negZ); if (!handle) return undefined; const ownership = params.key ? { key: params.key, iModel: this._iModel } : (params.isOwned ? "external" : undefined); tex = new Texture({ handle, ownership, type: params.type, transparency: TextureTransparency.Opaque }); this.addTexture(tex); return tex; } collectStatistics(stats) { for (const texture of this.textures.values()) if (texture instanceof Texture) stats.addTexture(texture.bytesUsed); for (const gradient of this.gradients) if (gradient instanceof Texture) stats.addTexture(gradient.bytesUsed); } } function getMaterialColor(color) { if (color instanceof ColorDef) return color; return color ? ColorDef.from(color.r, color.g, color.b) : undefined; } /** @internal */ export class System extends RenderSystem { canvas; currentRenderState = new RenderState(); context; frameBufferStack = new FrameBufferStack(); // frame buffers are not owned by the system _capabilities; resourceCache; glTimer; _textureBindings = []; _removeEventListener; // NB: Increase the size of these arrays when the maximum number of attributes used by any one shader increases. _curVertexAttribStates = [ 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, ]; _nextVertexAttribStates = [ 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, 0 /* VertexAttribState.Disabled */, ]; // The following are initialized immediately after the System is constructed. _lineCodeTexture; _noiseTexture; _techniques; _screenSpaceEffects; debugShaderFiles = []; static get instance() { return IModelApp.renderSystem; } get isValid() { return this.canvas !== undefined; } get lineCodeTexture() { return this._lineCodeTexture; } get noiseTexture() { return this._noiseTexture; } get techniques() { assert(undefined !== this._techniques); return this._techniques; } get screenSpaceEffects() { assert(undefined !== this._screenSpaceEffects); return this._screenSpaceEffects; } get maxTextureSize() { return this._capabilities.maxTextureSize; } get supportsCreateImageBitmap() { return this._capabilities.supportsCreateImageBitmap; } get maxRenderType() { return this._capabilities.maxRenderType; } get fragDepthDoesNotDisableEarlyZ() { return this._capabilities.driverBugs.fragDepthDoesNotDisableEarlyZ; } get maxAntialiasSamples() { return this._capabilities.maxAntialiasSamples; } get supportsNonPowerOf2Textures() { return this._capabilities.supportsNonPowerOf2Textures; } get maxTexSizeAllow() { return this._capabilities.maxTexSizeAllow; } get disjointTimerQuery() { const ext = this._capabilities.queryExtensionObject("EXT_disjoint_timer_query_webgl2"); return ext ?? this._capabilities.queryExtensionObject("EXT_disjoint_timer_query"); } get isMobile() { return this._capabilities.isMobile; } setDrawBuffers(attachments) { this.context.drawBuffers(attachments); } doIdleWork() { return this.techniques.idleCompileNextShader(); } /** Return a Promise which when resolved indicates that all pending external textures have finished loading from the backend. */ async waitForAllExternalTextures() { const extTexLoader = ExternalTextureLoader.instance; if (extTexLoader.numActiveRequests < 1 && extTexLoader.numPendingRequests < 1) return Promise.resolve(); const promise = new Promise((resolve) => { extTexLoader.onTexturesLoaded.addOnce(() => { resolve(); }); }); return promise; } get hasExternalTextureRequests() { const loader = ExternalTextureLoader.instance; return loader.numActiveRequests > 0 || loader.numPendingRequests > 0; } /** Attempt to create a WebGLRenderingContext, returning undefined if unsuccessful. */ static createContext(canvas, useWebGL2, inputContextAttributes) { if (!useWebGL2) return undefined; // WebGL 2 is required. let contextAttributes = { powerPreference: "high-performance" }; if (undefined !== inputContextAttributes) { // NOTE: Order matters with spread operator - if caller wants to override powerPreference, he should be able to. contextAttributes = { ...contextAttributes, ...inputContextAttributes }; } const context = canvas.getContext("webgl2", contextAttributes); return context ?? undefined; } static create(optionsIn) { const options = undefined !== optionsIn ? optionsIn : {}; const canvas = document.createElement("canvas"); if (null === canvas) throw new IModelError(BentleyStatus.ERROR, "Failed to obtain HTMLCanvasElement"); const context = this.createContext(canvas, true, optionsIn?.contextAttributes); if (undefined === context) throw new IModelError(BentleyStatus.ERROR, "Failed to obtain WebGL context"); if (!(context instanceof WebGL2RenderingContext)) throw new IModelError(BentleyStatus.ERROR, "WebGL 2 support is required"); const capabilities = Capabilities.create(context, options.disabledExtensions); if (undefined === capabilities) throw new IModelError(BentleyStatus.ERROR, "Failed to initialize rendering capabilities"); // set actual gl state to match desired state defaults context.depthFunc(GL.DepthFunc.Default); // LessOrEqual return new this(canvas, context, capabilities, options); } get isDisposed() { return undefined === this._techniques && undefined === this._lineCodeTexture && undefined === this._noiseTexture && undefined === this._screenSpaceEffects; } // Note: FrameBuffers inside of the FrameBufferStack are not owned by the System, and are only used as a central storage device dispose() { this._techniques = dispose(this._techniques); this._screenSpaceEffects = dispose(this._screenSpaceEffects); this._lineCodeTexture = dispose(this._lineCodeTexture); this._noiseTexture = dispose(this._noiseTexture); // We must attempt to dispose of each idmap in the resourceCache (if idmap is already disposed, has no effect) this.resourceCache.forEach((idMap) => { dispose(idMap); }); this.resourceCache.clear(); if (undefined !== this._removeEventListener) { this._removeEventListener(); this._removeEventListener = undefined; } } onInitialized() { this._techniques = Techniques.create(this.context); const noiseDim = 4; const noiseArr = new Uint8Array([152, 235, 94, 173, 219, 215, 115, 176, 73, 205, 43, 201, 10, 81, 205, 198]); this._noiseTexture = TextureHandle.createForData(noiseDim, noiseDim, noiseArr, false, GL.Texture.WrapMode.Repeat, GL.Texture.Format.Luminance); assert(undefined !== this._noiseTexture, "System.noiseTexture not created."); this._lineCodeTexture = TextureHandle.createForData(LineCode.size, LineCode.count, new Uint8Array(LineCode.lineCodeData), false, GL.Texture.WrapMode.Repeat, GL.Texture.Format.Luminance); assert(undefined !== this._lineCodeTexture, "System.lineCodeTexture not created."); this._screenSpaceEffects = new ScreenSpaceEffects(); } createTarget(canvas) { return new OnScreenTarget(canvas); } createOffscreenTarget(rect) { return new OffScreenTarget(rect); } createGraphic(options) { return new PrimitiveBuilder(this, options); } createPlanarGrid(frustum, grid) { return PlanarGridGeometry.create(frustum, grid, this); } createTerrainMesh(params, transform, disableTextureDisposal = false) { return RealityMeshGeometry.createForTerrain(params, transform, disableTextureDisposal); } createRealityMeshGraphic(params, disableTextureDisposal = false) { return RealityMeshGeometry.createGraphic(this, params, disableTextureDisposal); } createRealityMeshGeometry(realityMesh, disableTextureDisposal = false) { return RealityMeshGeometry.createFromRealityMesh(realityMesh, disableTextureDisposal); } createMeshGeometry(params, viOrigin) { return MeshRenderGeometry.create(params, viOrigin); } createPolylineGeometry(params, viOrigin) { return PolylineGeometry.create(params, viOrigin); } createPointStringGeometry(params, viOrigin) { return PointStringGeometry.create(params, viOrigin); } createAreaPattern(params) { return PatternBuffers.create(params); } createRenderInstances(params) { return RenderInstancesImpl.create(params); } createInstancedGraphic(geometry, instances) { const geom = geometry; return this.createRenderGraphic(geom, InstanceBuffers.fromRenderInstances(instances, geom.computeRange())); } createGraphicFromTemplate(args) { const template = args.template; const instances = args.instances; if (instances && !template.isInstanceable) { throw new Error("GraphicTemplate is not instanceable"); } const graphics = []; for (const node of template[_nodes]) { const nodeGraphics = []; for (const geometry of node.geometry) { const gf = instances ? this.createInstancedGraphic(geometry, instances) : this.createRenderGraphic(geometry); if (gf) { nodeGraphics.push(gf); } } if (nodeGraphics.length === 0) { continue; } if (node.transform) { const branch = new GraphicBranch(); for (const gf of nodeGraphics) { branch.add(gf); } graphics.push(this.createGraphicBranch(branch, node.transform)); } else { graphics.push(this.createGraphicList(nodeGraphics)); } } let graphic = this.createGraphicList(graphics); if (instances && instances[_featureTable]) { const range = new Range3d(); graphic.unionRange(range); graphic = this.createBatch(graphic, instances[_featureTable], range); } else if (template[_batch]) { graphic = this.createBatch(graphic, template[_batch].featureTable, template[_batch].range, template[_batch].options); } const templateBranch = template[_branch]; if (templateBranch) { const branch = new GraphicBranch(true); if (templateBranch.viewFlagOverrides) { branch.setViewFlagOverrides(templateBranch.viewFlagOverrides); } branch.add(graphic); graphic = this.createBranch(branch, templateBranch.transform ?? Transform.createIdentity()); } return graphic; } createRenderGraphic(geometry, instances) { const geom = geometry; let buffers; if (instances) { if (!geometry.isInstanceable) { throw new Error("RenderGeometry is not instanceable"); } if (instances instanceof PatternBuffers || instances instanceof InstanceBuffers) { buffers = instances; } else { assert(isInstancedGraphicParams(instances)); buffers = InstanceBuffers.fromParams(instances, () => geom.computeRange()); if (!buffers) { return undefined; } } } return geom.renderGeometryType === "mesh" ? MeshGraphic.create(geom, buffers) : Primitive.create(geom, buffers); } createPointCloudGeometry(args) { return new PointCloudGeometry(args); } createGraphicList(primitives) { return primitives.length === 1 ? primitives[0] : new GraphicsArray(primitives); } createGraphicBranch(branch, transform, options) { return new Branch(branch, transform, undefined, options); } createAnimationTransformNode(graphic, nodeId) { return new AnimationTransformBranch(graphic, nodeId); } createBatch(graphic, features, range, options) { return new Batch(graphic, features, range, options); } createGraphicOwner(owned) { return new GraphicOwner(owned); } createGraphicLayer(graphic, layerId) { return new Layer(graphic, layerId); } createGraphicLayerContainer(graphic, drawAsOverlay, transparency, elevation) { return new LayerContainer(graphic, drawAsOverlay, transparency, elevation); } createSkyBox(params) { if ("cube" === params.type) return SkyCubePrimitive.create(SkyBoxQuadsGeometry.create(params.texture)); return SkySpherePrimitive.create(SkySphereViewportQuadGeometry.createGeometry(params)); } createScreenSpaceEffectBuilder(params) { return createScreenSpaceEffectBuilder(params); } applyRenderState(newState) { newState.apply(this.currentRenderState); this.currentRenderState.copyFrom(newState); } createDepthBuffer(width, height, numSamples = 1) { // Note: The buffer/texture created here have ownership passed to the caller (system will not dispose of these) if (numSamples > 1) return RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.DEPTH24_STENCIL8, numSamples); else return TextureHandle.createForAttachment(width, height, GL.Texture.Format.DepthStencil, this.context.UNSIGNED_INT_24_8); } /** Returns the corresponding IdMap for an IModelConnection. Creates a new one if it doesn't exist. */ createIModelMap(imodel) { let idMap = this.resourceCache.get(imodel); if (!idMap) { idMap = new IdMap(imodel); this.resourceCache.set(imodel, idMap); } return idMap; } /** Removes an IModelConnection-IdMap pairing from the system's resource cache. */ removeIModelMap(imodel) { const idMap = this.resourceCache.get(imodel); if (idMap === undefined) return; dispose(idMap); this.resourceCache.delete(imodel); } createRenderMaterial(args) { if (args.source) { const cached = this.findMaterial(args.source.id, args.source.iModel); if (cached) return cached; } const params = new RenderMaterialParams(); params.alpha = args.alpha; if (undefined !== args.diffuse?.weight) params.diffuse = args.diffuse.weight; params.diffuseColor = getMaterialColor(args.diffuse?.color); if (args.specular) { params.specularColor = getMaterialColor(args.specular?.color); if (undefined !== args.specular.weight) params.specular = args.specular.weight; if (undefined !== args.specular.exponent) params.specularExponent = args.specular.exponent; } if (args.textureMapping) { params.textureMapping = new TextureMapping(args.textureMapping.texture, new TextureMapping.Params({ textureMat2x3: args.textureMapping.transform, mapMode: args.textureMapping.mode, textureWeight: args.textureMapping.weight, worldMapping: args.textureMapping.worldMapping, useConstantLod: args.textureMapping.useConstantLod, constantLodProps: args.textureMapping.constantLodProps, })); params.textureMapping.normalMapParams = args.textureMapping.normalMapParams; } if (args.source) { params.key = args.source.id; return this.getIdMap(args.source.iModel).getMaterial(params); } else { return new Material(params); } } /** Using its key, search for an existing material of an open iModel. */ findMaterial(key, imodel) { const idMap = this.resourceCache.get(imodel); if (!idMap) return undefined; return idMap.findMaterial(key); } getTextureCacheInfo(args) { const owner = undefined !== args.ownership && args.ownership !== "external" ? args.ownership : undefined; return owner ? { idMap: this.getIdMap(owner.iModel), key: owner.key } : undefined; } createTexture(args) { const info = this.getTextureCacheInfo(args); const existing = info?.idMap.findTexture(info?.key); if (existing) return existing; const type = args.type ?? RenderTexture.Type.Normal; const source = args.image.source; let handle; if (source instanceof ImageBuffer) handle = TextureHandle.createForImageBuffer(source, type); else if (source instanceof ImageBitmap) handle = TextureHandle.createForImageBitmap(source, type); else if (source instanceof HTMLImageElement) handle = TextureHandle.createForImage(source, type); else assert(false); if (!handle) return undefined; const texture = new Texture({ handle, type, ownership: args.ownership, transparency: args.image.transparency ?? TextureTransparency.Mixed }); if (texture && info) info.idMap.addTexture(texture, info.key); return texture; } async createTextureFromSource(args) { if (typeof args.ownership !== "object") return super.createTextureFromSource(args); return this.getIdMap(args.ownership.iModel).getTextureFromImageSource(args, args.ownership.key); } createTextureFromElement(id, imodel, params, format) { return this.getIdMap(imodel).getTextureFromElement(id, imodel, params, format); } createTextureFromCubeImages(posX, negX, posY, negY, posZ, negZ, imodel, params) { return this.getIdMap(imodel).getTextureFromCubeImages(posX, negX, posY, negY, posZ, negZ, params); } /** Attempt to create a texture using gradient symbology. */ getGradientTexture(symb, iModel) { let width = 0x100; let height = 0x100; if (symb.mode === Gradient.Mode.Thematic) { // Pixels in each row are identical, no point in having width > 1. width = 1; // We want maximum height to minimize bleeding of margin color. height = this.maxTextureSize; } const source = symb.produceImage({ width, height, includeThematicMargin: true }); return this.createTexture({ image: { source, transparency: ImageBufferFormat.Rgba === source.format ? TextureTransparency.Mixed : TextureTransparency.Opaque, }, ownership: iModel ? { iModel, key: symb } : undefined, type: RenderTexture.Type.Normal, }); } /** Using its key, search for an existing texture of an open iModel. */ findTexture(key, imodel) { const idMap = this.resourceCache.get(imodel); if (!idMap) return undefined; return idMap.findTexture(key); } createClipVolume(clipVector) { return ClipVolume.create(clipVector); } createBackgroundMapDrape(drapedTree, mapTree) { return BackgroundMapDrape.create(drapedTree, mapTree); } constructor(canvas, context, capabilities, options) { super(options); this.canvas = canvas; this.context = context; this._capabilities = capabilities; this.resourceCache = new Map(); this.glTimer = GLTimer.create(this); // Make this System a subscriber to the the IModelConnection onClose event this._removeEventListener = IModelConnection.onClose.addListener((imodel) => this.removeIModelMap(imodel)); canvas.addEventListener("webglcontextlost", async () => RenderSystem.contextLossHandler(), false); } /** Exposed strictly for tests. */ getIdMap(imodel) { const map = this.resourceCache.get(imodel); return undefined !== map ? map : this.createIModelMap(imodel); } bindTexture(unit, target, texture, makeActive) { const index = unit - TextureUnit.Zero; if (this._textureBindings[index] === texture) { if (makeActive) this.context.activeTexture(unit); return; } this._textureBindings[index] = texture; this.context.activeTexture(unit); this.context.bindTexture(target, undefined !== texture ? texture : null); } /** Bind the specified texture to the specified unit. This may *or may not* make the texture *active* */ bindTexture2d(unit, texture) { this.bindTexture(unit, GL.Texture.Target.TwoDee, texture, false); } /** Bind the specified texture to the specified unit. This may *or may not* make the texture *active* */ bindTextureCubeMap(unit, texture) { this.bindTexture(unit, GL.Texture.Target.CubeMap, texture, false); } /** Bind the specified texture to the specified unit. This *always* makes the texture *active* */ activateTexture2d(unit, texture) { this.bindTexture(unit, GL.Texture.Target.TwoDee, texture, true); } /** Bind the specified texture to the specified unit. This *always* makes the texture *active* */ activateTextureCubeMap(unit, texture) { this.bindTexture(unit, GL.Texture.Target.CubeMap, texture, true); } // Ensure *something* is bound to suppress 'no texture assigned to unit x' warnings. ensureSamplerBound(uniform, unit) { this.lineCodeTexture.bindSampler(uniform, unit); } get maxRealityImageryLayers() { return 6; } disposeTexture(texture) { System.instance.context.deleteTexture(texture); for (let i = 0; i < this._textureBindings.length; i++) { if (this._textureBindings[i] === texture) { this._textureBindings[i] = undefined; break; } } } // System keeps track of current enabled state of vertex attribute arrays. // This prevents errors caused by leaving a vertex attrib array enabled after disposing of the buffer bound to it; // also prevents unnecessarily 'updating' the enabled state of a vertex attrib array when it hasn't actually changed. enableVertexAttribArray(id, instanced) { assert(id < this._nextVertexAttribStates.length, "if you add new vertex attributes you must update array length"); assert(id < this._curVertexAttribStates.length, "if you add new vertex attributes you must update array length"); this._nextVertexAttribStates[id] = instanced ? 5 /* VertexAttribState.InstancedEnabled */ : 1 /* VertexAttribState.Enabled */; } updateVertexAttribArrays() { const cur = this._curVertexAttribStates; const next = this._nextVertexAttribStates; const context = this.context; for (let i = 0; i < next.length; i++) { const oldState = cur[i]; const newState = next[i]; if (oldState !== newState) { // Update the enabled state if it changed. const wasEnabled = 0 !== (1 /* VertexAttribState.Enabled */ & oldState); const nowEnabled = 0 !== (1 /* VertexAttribState.Enabled */ & newState); if (wasEnabled !== nowEnabled) { if (nowEnabled) { context.enableVertexAttribArray(i); } else { context.disableVertexAttribArray(i); } } // Only update the divisor if the attribute is enabled. if (nowEnabled) { const wasInstanced = 0 !== (4 /* VertexAttribState.Instanced */ & oldState); const nowInstanced = 0 !== (4 /* VertexAttribState.Instanced */ & newState); if (wasInstanced !== nowInstanced) { this.vertexAttribDivisor(i, nowInstanced ? 1 : 0); } } cur[i] = newState; } // Set the attribute back to disabled, but preserve the divisor. next[i] &= ~1 /* VertexAttribState.Enabled */; } } vertexAttribDivisor(index, divisor) { this.context.vertexAttribDivisor(index, divisor); } drawArrays(type, first, count, numInstances) { if (0 !== numInstances) { this.context.drawArraysInstanced(type, first, count, numInstances); } else { this.context.drawArrays(type, first, count); } } invalidateFrameBuffer(attachments) { this.context.invalidateFramebuffer(this.context.FRAMEBUFFER, attachments); } enableDiagnostics(enable) { enable = enable ?? RenderDiagnostics.All; Debug.printEnabled = RenderDiagnostics.None !== (enable & RenderDiagnostics.DebugOutput); Debug.evaluateEnabled = RenderDiagnostics.None !== (enable & RenderDiagnostics.WebGL); } // RenderSystemDebugControl get debugControl() { return this; } _dpiAwareLOD; get dpiAwareLOD() { return this._dpiAwareLOD ?? super.dpiAwareLOD; } set dpiAwareLOD(dpiAware) { this._dpiAwareLOD = dpiAware; } loseContext() { const ext = this._capabilities.queryExtensionObject("WEBGL_lose_context"); if (undefined === ext) return false; ext.loseContext(); return true; } compileAllShaders() { return this.techniques.compileShaders(); } get isGLTimerSupported() { return this.glTimer.isSupported; } set resultsCallback(callback) { this.glTimer.resultsCallback = callback; } collectStatistics(stats) { if (undefined !== this._lineCodeTexture) stats.addTexture(this._lineCodeTexture.bytesUsed); if (undefined !== this._noiseTexture) stats.addTexture(this._noiseTexture.bytesUsed); for (const idMap of this.resourceCache.values()) idMap.collectStatistics(stats); } setMaxAnisotropy(max) { this._capabilities.setMaxAnisotropy(max, this.context); } } //# sourceMappingURL=System.js.map