UNPKG

@itwin/core-frontend

Version:
767 lines • 37.3 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * 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 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.System = exports.IdMap = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_common_1 = require("@itwin/core-common"); const core_geometry_1 = require("@itwin/core-geometry"); const webgl_compatibility_1 = require("@itwin/webgl-compatibility"); const IModelApp_1 = require("../../../IModelApp"); const IModelConnection_1 = require("../../../IModelConnection"); const ImageUtil_1 = require("../../../common/ImageUtil"); const GraphicBranch_1 = require("../../../render/GraphicBranch"); const PrimitiveBuilder_1 = require("../../../internal/render/PrimitiveBuilder"); const RenderSystem_1 = require("../../../render/RenderSystem"); const RenderSystemDebugControl_1 = require("../RenderSystemDebugControl"); const BackgroundMapDrape_1 = require("./BackgroundMapDrape"); const CachedGeometry_1 = require("./CachedGeometry"); const ClipVolume_1 = require("./ClipVolume"); const Diagnostics_1 = require("./Diagnostics"); const FrameBuffer_1 = require("./FrameBuffer"); const GL_1 = require("./GL"); const GLTimer_1 = require("./GLTimer"); const Graphic_1 = require("./Graphic"); const InstancedGeometry_1 = require("./InstancedGeometry"); const Layer_1 = require("./Layer"); const LineCode_1 = require("./LineCode"); const Material_1 = require("./Material"); const Mesh_1 = require("./Mesh"); const PlanarGrid_1 = require("./PlanarGrid"); const PointCloud_1 = require("./PointCloud"); const PointString_1 = require("./PointString"); const Polyline_1 = require("./Polyline"); const Primitive_1 = require("./Primitive"); const RealityMesh_1 = require("./RealityMesh"); const RenderBuffer_1 = require("./RenderBuffer"); const RenderFlags_1 = require("./RenderFlags"); const RenderState_1 = require("./RenderState"); const ScreenSpaceEffect_1 = require("./ScreenSpaceEffect"); const Target_1 = require("./Target"); const Technique_1 = require("./Technique"); const Texture_1 = require("./Texture"); const Symbols_1 = require("../../../common/internal/Symbols"); /** Id map holds key value pairs for both materials and textures, useful for caching such objects. * @internal */ 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 core_bentley_1.Dictionary((lhs, rhs) => core_common_1.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) (0, core_bentley_1.dispose)(texture); for (const gradient of gradientArr) (0, core_bentley_1.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) { (0, core_bentley_1.assert)(texture instanceof Texture_1.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 || !core_bentley_1.Id64.isValidId64(params.key)) // Only cache persistent materials. return new Material_1.Material(params); let material = this.materials.get(params.key); if (!material) { material = new Material_1.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 = Texture_1.TextureHandle.createForElement(key, iModel, params.type, format, (_, data) => { if (tex) { (0, core_bentley_1.assert)(tex instanceof Texture_1.Texture); tex.transparency = data.transparency ?? core_common_1.TextureTransparency.Mixed; } }); if (!handle) return undefined; tex = new Texture_1.Texture({ handle, type: params.type, ownership: { key, iModel }, transparency: core_common_1.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 = core_common_1.ImageSourceFormat.Jpeg === args.source.format ? core_common_1.TextureTransparency.Opaque : (args.transparency ?? core_common_1.TextureTransparency.Mixed); try { const image = await (0, ImageUtil_1.imageElementFromImageSource)(args.source); if (!IModelApp_1.IModelApp.hasRenderSystem) return undefined; return IModelApp_1.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 = Texture_1.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_1.Texture({ handle, ownership, type: params.type, transparency: core_common_1.TextureTransparency.Opaque }); this.addTexture(tex); return tex; } collectStatistics(stats) { for (const texture of this.textures.values()) if (texture instanceof Texture_1.Texture) stats.addTexture(texture.bytesUsed); for (const gradient of this.gradients) if (gradient instanceof Texture_1.Texture) stats.addTexture(gradient.bytesUsed); } } exports.IdMap = IdMap; function getMaterialColor(color) { if (color instanceof core_common_1.ColorDef) return color; return color ? core_common_1.ColorDef.from(color.r, color.g, color.b) : undefined; } /** @internal */ class System extends RenderSystem_1.RenderSystem { canvas; currentRenderState = new RenderState_1.RenderState(); context; frameBufferStack = new FrameBuffer_1.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_1.IModelApp.renderSystem; } get isValid() { return this.canvas !== undefined; } get lineCodeTexture() { return this._lineCodeTexture; } get noiseTexture() { return this._noiseTexture; } get techniques() { (0, core_bentley_1.assert)(undefined !== this._techniques); return this._techniques; } get screenSpaceEffects() { (0, core_bentley_1.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 = Texture_1.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 = Texture_1.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 core_common_1.IModelError(core_bentley_1.BentleyStatus.ERROR, "Failed to obtain HTMLCanvasElement"); const context = this.createContext(canvas, true, optionsIn?.contextAttributes); if (undefined === context) throw new core_common_1.IModelError(core_bentley_1.BentleyStatus.ERROR, "Failed to obtain WebGL context"); if (!(context instanceof WebGL2RenderingContext)) throw new core_common_1.IModelError(core_bentley_1.BentleyStatus.ERROR, "WebGL 2 support is required"); const capabilities = webgl_compatibility_1.Capabilities.create(context, options.disabledExtensions); if (undefined === capabilities) throw new core_common_1.IModelError(core_bentley_1.BentleyStatus.ERROR, "Failed to initialize rendering capabilities"); // set actual gl state to match desired state defaults context.depthFunc(GL_1.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 = (0, core_bentley_1.dispose)(this._techniques); this._screenSpaceEffects = (0, core_bentley_1.dispose)(this._screenSpaceEffects); this._lineCodeTexture = (0, core_bentley_1.dispose)(this._lineCodeTexture); this._noiseTexture = (0, core_bentley_1.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) => { (0, core_bentley_1.dispose)(idMap); }); this.resourceCache.clear(); if (undefined !== this._removeEventListener) { this._removeEventListener(); this._removeEventListener = undefined; } } onInitialized() { this._techniques = Technique_1.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 = Texture_1.TextureHandle.createForData(noiseDim, noiseDim, noiseArr, false, GL_1.GL.Texture.WrapMode.Repeat, GL_1.GL.Texture.Format.Luminance); (0, core_bentley_1.assert)(undefined !== this._noiseTexture, "System.noiseTexture not created."); this._lineCodeTexture = Texture_1.TextureHandle.createForData(LineCode_1.LineCode.size, LineCode_1.LineCode.count, new Uint8Array(LineCode_1.LineCode.lineCodeData), false, GL_1.GL.Texture.WrapMode.Repeat, GL_1.GL.Texture.Format.Luminance); (0, core_bentley_1.assert)(undefined !== this._lineCodeTexture, "System.lineCodeTexture not created."); this._screenSpaceEffects = new ScreenSpaceEffect_1.ScreenSpaceEffects(); } createTarget(canvas) { return new Target_1.OnScreenTarget(canvas); } createOffscreenTarget(rect) { return new Target_1.OffScreenTarget(rect); } createGraphic(options) { return new PrimitiveBuilder_1.PrimitiveBuilder(this, options); } createPlanarGrid(frustum, grid) { return PlanarGrid_1.PlanarGridGeometry.create(frustum, grid, this); } createTerrainMesh(params, transform, disableTextureDisposal = false) { return RealityMesh_1.RealityMeshGeometry.createForTerrain(params, transform, disableTextureDisposal); } createRealityMeshGraphic(params, disableTextureDisposal = false) { return RealityMesh_1.RealityMeshGeometry.createGraphic(this, params, disableTextureDisposal); } createRealityMeshGeometry(realityMesh, disableTextureDisposal = false) { return RealityMesh_1.RealityMeshGeometry.createFromRealityMesh(realityMesh, disableTextureDisposal); } createMeshGeometry(params, viOrigin) { return Mesh_1.MeshRenderGeometry.create(params, viOrigin); } createPolylineGeometry(params, viOrigin) { return Polyline_1.PolylineGeometry.create(params, viOrigin); } createPointStringGeometry(params, viOrigin) { return PointString_1.PointStringGeometry.create(params, viOrigin); } createAreaPattern(params) { return InstancedGeometry_1.PatternBuffers.create(params); } createRenderInstances(params) { return InstancedGeometry_1.RenderInstancesImpl.create(params); } createInstancedGraphic(geometry, instances) { const geom = geometry; return this.createRenderGraphic(geom, InstancedGeometry_1.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[Symbols_1._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_1.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[Symbols_1._featureTable]) { const range = new core_geometry_1.Range3d(); graphic.unionRange(range); graphic = this.createBatch(graphic, instances[Symbols_1._featureTable], range); } else if (template[Symbols_1._batch]) { graphic = this.createBatch(graphic, template[Symbols_1._batch].featureTable, template[Symbols_1._batch].range, template[Symbols_1._batch].options); } const templateBranch = template[Symbols_1._branch]; if (templateBranch) { const branch = new GraphicBranch_1.GraphicBranch(true); if (templateBranch.viewFlagOverrides) { branch.setViewFlagOverrides(templateBranch.viewFlagOverrides); } branch.add(graphic); graphic = this.createBranch(branch, templateBranch.transform ?? core_geometry_1.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 InstancedGeometry_1.PatternBuffers || instances instanceof InstancedGeometry_1.InstanceBuffers) { buffers = instances; } else { (0, core_bentley_1.assert)((0, InstancedGeometry_1.isInstancedGraphicParams)(instances)); buffers = InstancedGeometry_1.InstanceBuffers.fromParams(instances, () => geom.computeRange()); if (!buffers) { return undefined; } } } return geom.renderGeometryType === "mesh" ? Mesh_1.MeshGraphic.create(geom, buffers) : Primitive_1.Primitive.create(geom, buffers); } createPointCloudGeometry(args) { return new PointCloud_1.PointCloudGeometry(args); } createGraphicList(primitives) { return primitives.length === 1 ? primitives[0] : new Graphic_1.GraphicsArray(primitives); } createGraphicBranch(branch, transform, options) { return new Graphic_1.Branch(branch, transform, undefined, options); } createAnimationTransformNode(graphic, nodeId) { return new Graphic_1.AnimationTransformBranch(graphic, nodeId); } createBatch(graphic, features, range, options) { return new Graphic_1.Batch(graphic, features, range, options); } createGraphicOwner(owned) { return new Graphic_1.GraphicOwner(owned); } createGraphicLayer(graphic, layerId) { return new Layer_1.Layer(graphic, layerId); } createGraphicLayerContainer(graphic, drawAsOverlay, transparency, elevation) { return new Layer_1.LayerContainer(graphic, drawAsOverlay, transparency, elevation); } createSkyBox(params) { if ("cube" === params.type) return Primitive_1.SkyCubePrimitive.create(CachedGeometry_1.SkyBoxQuadsGeometry.create(params.texture)); return Primitive_1.SkySpherePrimitive.create(CachedGeometry_1.SkySphereViewportQuadGeometry.createGeometry(params)); } createScreenSpaceEffectBuilder(params) { return (0, ScreenSpaceEffect_1.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 RenderBuffer_1.RenderBufferMultiSample.create(width, height, WebGL2RenderingContext.DEPTH24_STENCIL8, numSamples); else return Texture_1.TextureHandle.createForAttachment(width, height, GL_1.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; (0, core_bentley_1.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 core_common_1.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 core_common_1.TextureMapping(args.textureMapping.texture, new core_common_1.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_1.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 ?? core_common_1.RenderTexture.Type.Normal; const source = args.image.source; let handle; if (source instanceof core_common_1.ImageBuffer) handle = Texture_1.TextureHandle.createForImageBuffer(source, type); else if (source instanceof ImageBitmap) handle = Texture_1.TextureHandle.createForImageBitmap(source, type); else if (source instanceof HTMLImageElement) handle = Texture_1.TextureHandle.createForImage(source, type); else (0, core_bentley_1.assert)(false); if (!handle) return undefined; const texture = new Texture_1.Texture({ handle, type, ownership: args.ownership, transparency: args.image.transparency ?? core_common_1.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 === core_common_1.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: core_common_1.ImageBufferFormat.Rgba === source.format ? core_common_1.TextureTransparency.Mixed : core_common_1.TextureTransparency.Opaque, }, ownership: iModel ? { iModel, key: symb } : undefined, type: core_common_1.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_1.ClipVolume.create(clipVector); } createBackgroundMapDrape(drapedTree, mapTree) { return BackgroundMapDrape_1.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_1.GLTimer.create(this); // Make this System a subscriber to the the IModelConnection onClose event this._removeEventListener = IModelConnection_1.IModelConnection.onClose.addListener((imodel) => this.removeIModelMap(imodel)); canvas.addEventListener("webglcontextlost", async () => RenderSystem_1.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 - RenderFlags_1.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_1.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_1.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_1.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_1.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) { (0, core_bentley_1.assert)(id < this._nextVertexAttribStates.length, "if you add new vertex attributes you must update array length"); (0, core_bentley_1.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 ?? RenderSystemDebugControl_1.RenderDiagnostics.All; Diagnostics_1.Debug.printEnabled = RenderSystemDebugControl_1.RenderDiagnostics.None !== (enable & RenderSystemDebugControl_1.RenderDiagnostics.DebugOutput); Diagnostics_1.Debug.evaluateEnabled = RenderSystemDebugControl_1.RenderDiagnostics.None !== (enable & RenderSystemDebugControl_1.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); } } exports.System = System; //# sourceMappingURL=System.js.map