@itwin/core-frontend
Version:
iTwin.js frontend components
767 lines • 37.3 kB
JavaScript
"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) {
(0, core_bentley_1.expectDefined)(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