UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

860 lines (859 loc) 41.2 kB
import { getDimensionsFromTextureSize, textureSizeIsObject } from "../Materials/Textures/textureCreationOptions.js"; import { Texture } from "../Materials/Textures/texture.js"; import { backbufferColorTextureHandle, backbufferDepthStencilTextureHandle } from "./frameGraphTypes.js"; import { FrameGraphRenderTarget } from "./frameGraphRenderTarget.js"; import { FrameGraphRenderPass } from "./Passes/renderPass.js"; import { Logger } from "../Misc/logger.js"; import { GetTypeForDepthTexture, IsDepthTexture, HasStencilAspect } from "../Materials/Textures/textureHelper.functions.js"; var FrameGraphTextureNamespace; (function (FrameGraphTextureNamespace) { FrameGraphTextureNamespace[FrameGraphTextureNamespace["Task"] = 0] = "Task"; FrameGraphTextureNamespace[FrameGraphTextureNamespace["Graph"] = 1] = "Graph"; FrameGraphTextureNamespace[FrameGraphTextureNamespace["External"] = 2] = "External"; })(FrameGraphTextureNamespace || (FrameGraphTextureNamespace = {})); /** * Manages the textures used by a frame graph * @experimental */ export class FrameGraphTextureManager { /** * Constructs a new instance of the texture manager * @param engine The engine to use * @param _debugTextures If true, debug textures will be created so that they are visible in the inspector * @param _scene The scene the manager belongs to */ constructor(engine, _debugTextures = false, _scene) { this.engine = engine; this._debugTextures = _debugTextures; this._scene = _scene; /** @internal */ this._textures = new Map(); /** @internal */ this._historyTextures = new Map(); /** @internal */ this._isRecordingTask = false; /** * Gets or sets a boolean indicating if debug logs should be shown when applying texture allocation optimization (default: false) */ this.showDebugLogsForTextureAllcationOptimization = false; this._addSystemTextures(); } /** * Checks if a handle is a backbuffer handle (color or depth/stencil) * @param handle The handle to check * @returns True if the handle is a backbuffer handle */ isBackbuffer(handle) { if (handle === backbufferColorTextureHandle || handle === backbufferDepthStencilTextureHandle) { return true; } const textureEntry = this._textures.get(handle); if (!textureEntry) { return false; } return textureEntry.refHandle === backbufferColorTextureHandle || textureEntry.refHandle === backbufferDepthStencilTextureHandle; } /** * Checks if a handle is a backbuffer color handle * @param handle The handle to check * @returns True if the handle is a backbuffer color handle */ isBackbufferColor(handle) { if (handle === backbufferColorTextureHandle) { return true; } const textureEntry = this._textures.get(handle); if (!textureEntry) { return false; } return textureEntry.refHandle === backbufferColorTextureHandle; } /** * Checks if a handle is a backbuffer depth/stencil handle * @param handle The handle to check * @returns True if the handle is a backbuffer depth/stencil handle */ isBackbufferDepthStencil(handle) { if (handle === backbufferDepthStencilTextureHandle) { return true; } const textureEntry = this._textures.get(handle); if (!textureEntry) { return false; } return textureEntry.refHandle === backbufferDepthStencilTextureHandle; } /** * Checks if a handle is a history texture (or points to a history texture, for a dangling handle) * @param handle The handle to check * @returns True if the handle is a history texture, otherwise false */ isHistoryTexture(handle) { const entry = this._textures.get(handle); if (!entry) { return false; } handle = entry.refHandle ?? handle; return this._historyTextures.has(handle); } /** * Gets the creation options of a texture * @param handle Handle of the texture * @returns The creation options of the texture */ getTextureCreationOptions(handle) { const entry = this._textures.get(handle); const creationOptions = entry.creationOptions; return { size: textureSizeIsObject(creationOptions.size) ? { ...creationOptions.size } : creationOptions.size, sizeIsPercentage: creationOptions.sizeIsPercentage, options: FrameGraphTextureManager.CloneTextureOptions(creationOptions.options, entry.textureIndex), isHistoryTexture: creationOptions.isHistoryTexture, }; } /** * Gets the description of a texture * @param handle Handle of the texture * @returns The description of the texture */ getTextureDescription(handle) { const creationOptions = this.getTextureCreationOptions(handle); const size = !creationOptions.sizeIsPercentage ? textureSizeIsObject(creationOptions.size) ? creationOptions.size : { width: creationOptions.size, height: creationOptions.size } : this.getAbsoluteDimensions(creationOptions.size); return { size, options: creationOptions.options, }; } /** * Gets a texture handle or creates a new texture if the handle is not provided. * If handle is not provided, newTextureName and creationOptions must be provided. * @param handle If provided, will simply return the handle * @param newTextureName Name of the new texture to create * @param creationOptions Options to use when creating the new texture * @returns The handle to the texture. */ getTextureHandleOrCreateTexture(handle, newTextureName, creationOptions) { if (handle === undefined) { if (newTextureName === undefined || creationOptions === undefined) { throw new Error("getTextureHandleOrCreateTexture: Either handle or newTextureName and creationOptions must be provided."); } return this.createRenderTargetTexture(newTextureName, creationOptions); } return handle; } /** * Gets a texture from a handle. * Note that if the texture is a history texture, the read texture for the current frame will be returned. * @param handle The handle of the texture * @returns The texture or null if not found */ getTextureFromHandle(handle) { const historyEntry = this._historyTextures.get(handle); if (historyEntry) { return historyEntry.textures[historyEntry.index ^ 1]; // gets the read texture } return this._textures.get(handle).texture; } /** * Imports a texture into the texture manager * @param name Name of the texture * @param texture Texture to import * @param handle Existing handle to use for the texture. If not provided (default), a new handle will be created. * @returns The handle to the texture */ importTexture(name, texture, handle) { if (handle !== undefined) { this._freeEntry(handle); } const creationOptions = { size: { width: texture.width, height: texture.height }, sizeIsPercentage: false, isHistoryTexture: false, options: { createMipMaps: texture.generateMipMaps, samples: texture.samples, types: [texture.type], formats: [texture.format], useSRGBBuffers: [texture._useSRGBBuffer], creationFlags: [texture._creationFlags], labels: texture.label ? [texture.label] : ["imported"], }, }; return this._createHandleForTexture(name, texture, creationOptions, FrameGraphTextureNamespace.External, handle); } /** * Creates a new render target texture * If multiple textures are described in FrameGraphTextureCreationOptions, the handle of the first texture is returned, handle+1 is the handle of the second texture, etc. * @param name Name of the texture * @param creationOptions Options to use when creating the texture * @param handle Existing handle to use for the texture. If not provided (default), a new handle will be created. * @returns The handle to the texture */ createRenderTargetTexture(name, creationOptions, handle) { return this._createHandleForTexture(name, null, { size: textureSizeIsObject(creationOptions.size) ? { ...creationOptions.size } : creationOptions.size, sizeIsPercentage: creationOptions.sizeIsPercentage, isHistoryTexture: creationOptions.isHistoryTexture, options: FrameGraphTextureManager.CloneTextureOptions(creationOptions.options, undefined, true), }, this._isRecordingTask ? FrameGraphTextureNamespace.Task : FrameGraphTextureNamespace.Graph, handle); } /** * Creates a (frame graph) render target wrapper * Note that renderTargets or renderTargetDepth can be undefined, but not both at the same time! * @param name Name of the render target wrapper * @param renderTargets Render target handles (textures) to use * @param renderTargetDepth Render target depth handle (texture) to use * @param depthReadOnly If true, the depth buffer will be read-only * @param stencilReadOnly If true, the stencil buffer will be read-only * @returns The created render target wrapper */ createRenderTarget(name, renderTargets, renderTargetDepth, depthReadOnly, stencilReadOnly) { const renderTarget = new FrameGraphRenderTarget(name, this, renderTargets, renderTargetDepth); const rtw = renderTarget.renderTargetWrapper; if (rtw !== undefined) { rtw.depthReadOnly = !!depthReadOnly; rtw.stencilReadOnly = !!stencilReadOnly; if (renderTargets) { const handles = Array.isArray(renderTargets) ? renderTargets : [renderTargets]; for (let i = 0; i < handles.length; i++) { let handle = handles[i]; handle = this._textures.get(handle)?.refHandle ?? handle; const historyEntry = this._historyTextures.get(handle); if (historyEntry) { historyEntry.references.push({ renderTargetWrapper: rtw, textureIndex: i }); rtw.setTexture(historyEntry.textures[historyEntry.index], i, false); } } } } return renderTarget; } /** * Creates a handle which is not associated with any texture. * Call resolveDanglingHandle to associate the handle with a valid texture handle. * @returns The dangling handle */ createDanglingHandle() { return FrameGraphTextureManager._Counter++; } /** * Associates a texture with a dangling handle * @param danglingHandle The dangling handle * @param handle The handle to associate with the dangling handle (if not provided, a new texture handle will be created, using the newTextureName and creationOptions parameters) * @param newTextureName The name of the new texture to create (if handle is not provided) * @param creationOptions The options to use when creating the new texture (if handle is not provided) */ resolveDanglingHandle(danglingHandle, handle, newTextureName, creationOptions) { if (handle === undefined) { if (newTextureName === undefined || creationOptions === undefined) { throw new Error("resolveDanglingHandle: Either handle or newTextureName and creationOptions must be provided."); } this.createRenderTargetTexture(newTextureName, creationOptions, danglingHandle); return; } const textureEntry = this._textures.get(handle); if (textureEntry === undefined) { throw new Error(`resolveDanglingHandle: Handle ${handle} does not exist!`); } this._textures.set(danglingHandle, { texture: textureEntry.texture, refHandle: handle, name: textureEntry.name, creationOptions: { size: { ...textureEntry.creationOptions.size }, options: FrameGraphTextureManager.CloneTextureOptions(textureEntry.creationOptions.options), sizeIsPercentage: textureEntry.creationOptions.sizeIsPercentage, isHistoryTexture: false, }, namespace: textureEntry.namespace, textureIndex: textureEntry.textureIndex, }); } /** * Gets the absolute dimensions of a texture. * @param size The size of the texture. Width and height must be expressed as a percentage of the screen size (100=100%)! * @param screenWidth The width of the screen (default: the width of the rendering canvas) * @param screenHeight The height of the screen (default: the height of the rendering canvas) * @returns The absolute dimensions of the texture */ getAbsoluteDimensions(size, screenWidth = this.engine.getRenderWidth(true), screenHeight = this.engine.getRenderHeight(true)) { const { width, height } = getDimensionsFromTextureSize(size); return { width: Math.floor((width * screenWidth) / 100), height: Math.floor((height * screenHeight) / 100), }; } /** * Calculates the total byte size of all textures used by the frame graph texture manager (including external textures) * @param optimizedSize True if the calculation should not factor in aliased textures * @param outputWidth The output width of the frame graph. Will be used to calculate the size of percentage-based textures * @param outputHeight The output height of the frame graph. Will be used to calculate the size of percentage-based textures * @returns The total size of all textures */ computeTotalTextureSize(optimizedSize, outputWidth, outputHeight) { let totalSize = 0; this._textures.forEach((entry, handle) => { if (handle === backbufferColorTextureHandle || handle === backbufferDepthStencilTextureHandle || entry.refHandle !== undefined) { return; } if (optimizedSize && entry.aliasHandle !== undefined) { return; } const options = entry.creationOptions; const textureIndex = entry.textureIndex || 0; const dimensions = options.sizeIsPercentage ? this.getAbsoluteDimensions(options.size, outputWidth, outputHeight) : getDimensionsFromTextureSize(options.size); const blockInfo = FrameGraphTextureManager._GetTextureBlockInformation(options.options.types?.[textureIndex] ?? 0, options.options.formats[textureIndex]); const textureByteSize = Math.ceil(dimensions.width / blockInfo.width) * Math.ceil(dimensions.height / blockInfo.height) * blockInfo.length; let byteSize = textureByteSize; if (options.options.createMipMaps) { byteSize = Math.floor((byteSize * 4) / 3); } if ((options.options.samples || 1) > 1) { // We need an additional texture in the case of MSAA byteSize += textureByteSize; } totalSize += byteSize; }); return totalSize; } /** @internal */ _dispose() { this._releaseTextures(); } /** @internal */ _allocateTextures(tasks) { if (tasks) { this._optimizeTextureAllocation(tasks); } this._textures.forEach((entry) => { if (!entry.texture) { if (entry.refHandle !== undefined) { // entry is a dangling handle which has been resolved to point to refHandle // We simply update the texture to point to the refHandle texture const refEntry = this._textures.get(entry.refHandle); entry.texture = refEntry.texture; if (refEntry.refHandle === backbufferColorTextureHandle) { entry.refHandle = backbufferColorTextureHandle; } if (refEntry.refHandle === backbufferDepthStencilTextureHandle) { entry.refHandle = backbufferDepthStencilTextureHandle; } } else if (entry.namespace !== FrameGraphTextureNamespace.External) { if (entry.aliasHandle !== undefined) { const aliasEntry = this._textures.get(entry.aliasHandle); entry.texture = aliasEntry.texture; entry.texture.incrementReferences(); } else { const creationOptions = entry.creationOptions; const size = creationOptions.sizeIsPercentage ? this.getAbsoluteDimensions(creationOptions.size) : creationOptions.size; const textureIndex = entry.textureIndex || 0; const internalTextureCreationOptions = { createMipMaps: creationOptions.options.createMipMaps, samples: creationOptions.options.samples, type: creationOptions.options.types?.[textureIndex], format: creationOptions.options.formats?.[textureIndex], useSRGBBuffer: creationOptions.options.useSRGBBuffers?.[textureIndex], creationFlags: creationOptions.options.creationFlags?.[textureIndex], label: creationOptions.options.labels?.[textureIndex] ?? `${entry.name}${textureIndex > 0 ? "#" + textureIndex : ""}`, samplingMode: 1, createMSAATexture: creationOptions.options.samples > 1, }; const isDepthTexture = IsDepthTexture(internalTextureCreationOptions.format); const hasStencil = HasStencilAspect(internalTextureCreationOptions.format); const source = isDepthTexture && hasStencil ? 12 /* InternalTextureSource.DepthStencil */ : isDepthTexture || hasStencil ? 14 /* InternalTextureSource.Depth */ : 5 /* InternalTextureSource.RenderTarget */; const internalTexture = this.engine._createInternalTexture(size, internalTextureCreationOptions, false, source); if (isDepthTexture) { internalTexture.type = GetTypeForDepthTexture(internalTexture.format); } entry.texture = internalTexture; } } } if (entry.texture && entry.refHandle === undefined) { entry.debug?.dispose(); entry.debug = this._createDebugTexture(entry.name, entry.texture); } }); this._historyTextures.forEach((entry) => { for (let i = 0; i < entry.handles.length; i++) { entry.textures[i] = this._textures.get(entry.handles[i]).texture; } }); } /** @internal */ _releaseTextures(releaseAll = true) { this._textures.forEach((entry, handle) => { if (entry.lifespan) { entry.lifespan.firstTask = Number.MAX_VALUE; entry.lifespan.lastTask = 0; } entry.aliasHandle = undefined; if (releaseAll || entry.namespace !== FrameGraphTextureNamespace.External) { entry.debug?.dispose(); entry.debug = undefined; } if (entry.namespace === FrameGraphTextureNamespace.External) { return; } entry.texture?.dispose(); entry.texture = null; if (releaseAll || entry.namespace === FrameGraphTextureNamespace.Task) { this._textures.delete(handle); } }); this._historyTextures.forEach((entry) => { for (let i = 0; i < entry.handles.length; i++) { entry.textures[i] = null; } }); if (releaseAll) { this._textures.clear(); this._historyTextures.clear(); this._addSystemTextures(); } } /** @internal */ _updateHistoryTextures() { this._historyTextures.forEach((entry) => { entry.index = entry.index ^ 1; const currentTexture = entry.textures[entry.index]; if (currentTexture) { for (const { renderTargetWrapper, textureIndex } of entry.references) { renderTargetWrapper.setTexture(currentTexture, textureIndex, false); } } }); } _addSystemTextures() { const size = { width: this.engine.getRenderWidth(true), height: this.engine.getRenderHeight(true) }; this._textures.set(backbufferColorTextureHandle, { name: "backbuffer color", texture: null, creationOptions: { size, options: { createMipMaps: false, samples: this.engine.getCreationOptions().antialias ? 4 : 1, types: [0], // todo? get from engine formats: [5], // todo? get from engine useSRGBBuffers: [false], creationFlags: [0], labels: ["backbuffer color"], }, sizeIsPercentage: false, }, namespace: FrameGraphTextureNamespace.External, }); this._textures.set(backbufferDepthStencilTextureHandle, { name: "backbuffer depth/stencil", texture: null, creationOptions: { size, options: { createMipMaps: false, samples: this.engine.getCreationOptions().antialias ? 4 : 1, types: [0], // todo? get from engine formats: [16], // todo? get from engine useSRGBBuffers: [false], creationFlags: [0], labels: ["backbuffer depth/stencil"], }, sizeIsPercentage: false, }, namespace: FrameGraphTextureNamespace.External, }); } _createDebugTexture(name, texture) { if (!this._debugTextures) { return; } const textureDebug = new Texture(null, this._scene); textureDebug.name = name; textureDebug._texture = texture; textureDebug._texture.incrementReferences(); return textureDebug; } _freeEntry(handle) { const entry = this._textures.get(handle); if (entry) { entry.debug?.dispose(); this._textures.delete(handle); } } _createHandleForTexture(name, texture, creationOptions, namespace, handle, textureIndex) { handle = handle ?? FrameGraphTextureManager._Counter++; textureIndex = textureIndex || 0; const textureName = creationOptions.isHistoryTexture ? `${name} ping` : name; let label = creationOptions.options.labels?.[textureIndex] ?? ""; if (label === textureName) { label = ""; } const textureEntry = { texture, name: `${textureName}${label ? " " + label : ""}`, creationOptions: { size: textureSizeIsObject(creationOptions.size) ? creationOptions.size : { width: creationOptions.size, height: creationOptions.size }, options: creationOptions.options, sizeIsPercentage: creationOptions.sizeIsPercentage, isHistoryTexture: creationOptions.isHistoryTexture, }, namespace, textureIndex, textureDescriptionHash: this._createTextureDescriptionHash(creationOptions), lifespan: { firstTask: Number.MAX_VALUE, lastTask: 0, }, }; this._textures.set(handle, textureEntry); if (namespace === FrameGraphTextureNamespace.External) { return handle; } if (creationOptions.isHistoryTexture) { const pongCreationOptions = { size: { ...textureEntry.creationOptions.size }, options: { ...textureEntry.creationOptions.options }, sizeIsPercentage: textureEntry.creationOptions.sizeIsPercentage, isHistoryTexture: false, }; const pongTexture = this._createHandleForTexture(`${name} pong`, null, pongCreationOptions, namespace); this._historyTextures.set(handle, { textures: [null, null], handles: [handle, pongTexture], index: 0, references: [] }); return handle; } if (creationOptions.options.types && creationOptions.options.types.length > 1 && textureIndex === 0) { const textureCount = creationOptions.options.types.length; const creationOptionsForTexture = { size: textureSizeIsObject(creationOptions.size) ? creationOptions.size : { width: creationOptions.size, height: creationOptions.size }, options: creationOptions.options, sizeIsPercentage: creationOptions.sizeIsPercentage, }; for (let i = 1; i < textureCount; i++) { this._createHandleForTexture(textureName, null, creationOptionsForTexture, namespace, handle + i, i); } FrameGraphTextureManager._Counter += textureCount - 1; } return handle; } _createTextureDescriptionHash(options) { const hash = []; hash.push(textureSizeIsObject(options.size) ? `${options.size.width}_${options.size.height}` : `${options.size}`); hash.push(options.sizeIsPercentage ? "%" : "A"); hash.push(options.options.createMipMaps ? "M" : "N"); hash.push(options.options.samples ? `${options.options.samples}` : "S1"); hash.push(options.options.types ? options.options.types.join("_") : `${0}`); hash.push(options.options.formats ? options.options.formats.join("_") : `${5}`); hash.push(options.options.useSRGBBuffers ? options.options.useSRGBBuffers.join("_") : "false"); hash.push(options.options.creationFlags ? options.options.creationFlags.join("_") : "0"); return hash.join("_"); } _optimizeTextureAllocation(tasks) { this._computeTextureLifespan(tasks); if (this.showDebugLogsForTextureAllcationOptimization) { Logger.Log(`================== Optimization of texture allocation ==================`); } const cache = new Map(); const iterator = this._textures.keys(); for (let key = iterator.next(); key.done !== true; key = iterator.next()) { const textureHandle = key.value; const textureEntry = this._textures.get(textureHandle); if (textureEntry.refHandle !== undefined || textureEntry.namespace === FrameGraphTextureNamespace.External || this._historyTextures.has(textureHandle)) { continue; } const textureHash = textureEntry.textureDescriptionHash; const textureLifespan = textureEntry.lifespan; const cacheEntries = cache.get(textureHash); if (cacheEntries) { let cacheEntryFound = false; for (const cacheEntry of cacheEntries) { const [sourceHandle, lifespanArray] = cacheEntry; let overlapped = false; for (const lifespan of lifespanArray) { if (lifespan.firstTask <= textureLifespan.lastTask && lifespan.lastTask >= textureLifespan.firstTask) { overlapped = true; break; } } if (!overlapped) { // No overlap between texture lifespan and all lifespans in the array, this texture can reuse the same entry cache if (this.showDebugLogsForTextureAllcationOptimization) { Logger.Log(`Texture ${textureHandle} (${textureEntry.name}) reuses cache entry ${sourceHandle}`); } lifespanArray.push(textureLifespan); textureEntry.aliasHandle = sourceHandle; cacheEntryFound = true; break; } } if (!cacheEntryFound) { cacheEntries.push([textureHandle, [textureLifespan]]); } } else { cache.set(textureHash, [[textureHandle, [textureLifespan]]]); } } } // Loop through all task/pass dependencies and compute the lifespan of each texture (that is, the first task/pass that uses it and the last task/pass that uses it) _computeTextureLifespan(tasks) { if (this.showDebugLogsForTextureAllcationOptimization) { Logger.Log(`================== Dump of texture dependencies for all tasks/passes ==================`); } for (let t = 0; t < tasks.length; ++t) { const task = tasks[t]; if (task.passes.length > 0) { this._computeTextureLifespanForPasses(task, t, task.passes); } if (task.passesDisabled.length > 0) { this._computeTextureLifespanForPasses(task, t, task.passesDisabled); } if (task.dependencies) { if (this.showDebugLogsForTextureAllcationOptimization) { Logger.Log(`task#${t} (${task.name}), global dependencies`); } this._updateLifespan(t * 100 + 99, task.dependencies); } } if (this.showDebugLogsForTextureAllcationOptimization) { Logger.Log(`================== Texture lifespans ==================`); const iterator = this._textures.keys(); for (let key = iterator.next(); key.done !== true; key = iterator.next()) { const textureHandle = key.value; const textureEntry = this._textures.get(textureHandle); if (textureEntry.refHandle !== undefined || textureEntry.namespace === FrameGraphTextureNamespace.External || this._historyTextures.has(textureHandle)) { continue; } Logger.Log(`${textureHandle} (${textureEntry.name}): ${textureEntry.lifespan.firstTask} - ${textureEntry.lifespan.lastTask}`); } } } _computeTextureLifespanForPasses(task, taskIndex, passes) { for (let p = 0; p < passes.length; ++p) { const dependencies = new Set(); const pass = passes[p]; if (!FrameGraphRenderPass.IsRenderPass(pass)) { continue; } pass.collectDependencies(dependencies); if (this.showDebugLogsForTextureAllcationOptimization) { Logger.Log(`task#${taskIndex} (${task.name}), pass#${p} (${pass.name})`); } this._updateLifespan(taskIndex * 100 + p, dependencies); } } _updateLifespan(passOrderNum, dependencies) { const iterator = dependencies.keys(); for (let key = iterator.next(); key.done !== true; key = iterator.next()) { const textureHandle = key.value; let textureEntry = this._textures.get(textureHandle); if (!textureEntry) { throw new Error(`FrameGraph._computeTextureLifespan: Texture handle "${textureHandle}" not found in the texture manager.`); } let handle = textureHandle; while (textureEntry.refHandle !== undefined) { handle = textureEntry.refHandle; textureEntry = this._textures.get(handle); if (!textureEntry) { throw new Error(`FrameGraph._computeTextureLifespan: Texture handle "${handle}" not found in the texture manager (source handle="${textureHandle}").`); } } if (textureEntry.namespace === FrameGraphTextureNamespace.External || this._historyTextures.has(handle)) { continue; } if (this.showDebugLogsForTextureAllcationOptimization) { Logger.Log(` ${handle} (${textureEntry.name})`); } textureEntry.lifespan.firstTask = Math.min(textureEntry.lifespan.firstTask, passOrderNum); textureEntry.lifespan.lastTask = Math.max(textureEntry.lifespan.lastTask, passOrderNum); } } /** * Clones a texture options * @param options The options to clone * @param textureIndex The index of the texture in the types, formats, etc array of FrameGraphTextureOptions. If not provided, all options are cloned. * @param preserveLabels True if the labels should be preserved (default: false) * @returns The cloned options */ static CloneTextureOptions(options, textureIndex, preserveLabels) { return textureIndex !== undefined ? { createMipMaps: options.createMipMaps, samples: options.samples, types: options.types ? [options.types[textureIndex]] : undefined, formats: options.formats ? [options.formats[textureIndex]] : undefined, useSRGBBuffers: options.useSRGBBuffers ? [options.useSRGBBuffers[textureIndex]] : undefined, creationFlags: options.creationFlags ? [options.creationFlags[textureIndex]] : undefined, labels: options.labels ? [options.labels[textureIndex]] : undefined, } : { createMipMaps: options.createMipMaps, samples: options.samples, types: options.types ? [...options.types] : undefined, formats: options.formats ? [...options.formats] : undefined, useSRGBBuffers: options.useSRGBBuffers ? [...options.useSRGBBuffers] : undefined, creationFlags: options.creationFlags ? [...options.creationFlags] : undefined, labels: options.labels && preserveLabels ? [...options.labels] : undefined, }; } /** * Gets the texture block information. * @param type Type of the texture. * @param format Format of the texture. * @returns The texture block information. You can calculate the byte size of the texture by doing: Math.ceil(width / blockInfo.width) * Math.ceil(height / blockInfo.height) * blockInfo.length */ static _GetTextureBlockInformation(type, format) { switch (format) { case 15: return { width: 1, height: 1, length: 2 }; case 16: return { width: 1, height: 1, length: 3 }; case 13: return { width: 1, height: 1, length: 4 }; case 14: return { width: 1, height: 1, length: 4 }; case 18: return { width: 1, height: 1, length: 5 }; case 19: return { width: 1, height: 1, length: 1 }; case 36492: return { width: 4, height: 4, length: 16 }; case 36495: return { width: 4, height: 4, length: 16 }; case 36494: return { width: 4, height: 4, length: 16 }; case 33779: return { width: 4, height: 4, length: 16 }; case 33778: return { width: 4, height: 4, length: 16 }; case 33777: case 33776: return { width: 4, height: 4, length: 8 }; case 37808: return { width: 4, height: 4, length: 16 }; case 36196: case 37492: return { width: 4, height: 4, length: 8 }; case 37496: return { width: 4, height: 4, length: 16 }; } switch (type) { case 3: case 0: switch (format) { case 6: case 8: case 0: case 1: case 2: return { width: 1, height: 1, length: 1 }; case 7: case 9: return { width: 1, height: 1, length: 2 }; case 4: case 10: return { width: 1, height: 1, length: 3 }; case 11: return { width: 1, height: 1, length: 4 }; default: return { width: 1, height: 1, length: 4 }; } case 4: case 5: switch (format) { case 8: return { width: 1, height: 1, length: 2 }; case 9: return { width: 1, height: 1, length: 4 }; case 10: return { width: 1, height: 1, length: 6 }; case 11: return { width: 1, height: 1, length: 8 }; default: return { width: 1, height: 1, length: 8 }; } case 6: case 7: // Refers to UNSIGNED_INT switch (format) { case 8: return { width: 1, height: 1, length: 4 }; case 9: return { width: 1, height: 1, length: 8 }; case 10: return { width: 1, height: 1, length: 12 }; case 11: return { width: 1, height: 1, length: 16 }; default: return { width: 1, height: 1, length: 16 }; } case 1: switch (format) { case 6: return { width: 1, height: 1, length: 4 }; case 7: return { width: 1, height: 1, length: 8 }; case 4: return { width: 1, height: 1, length: 12 }; case 5: return { width: 1, height: 1, length: 16 }; default: return { width: 1, height: 1, length: 16 }; } case 2: switch (format) { case 6: return { width: 1, height: 1, length: 2 }; case 7: return { width: 1, height: 1, length: 4 }; case 4: return { width: 1, height: 1, length: 6 }; case 5: return { width: 1, height: 1, length: 8 }; default: return { width: 1, height: 1, length: 8 }; } case 10: return { width: 1, height: 1, length: 2 }; case 13: switch (format) { case 5: case 11: return { width: 1, height: 1, length: 4 }; default: return { width: 1, height: 1, length: 4 }; } case 14: switch (format) { case 5: case 11: return { width: 1, height: 1, length: 4 }; default: return { width: 1, height: 1, length: 4 }; } case 8: return { width: 1, height: 1, length: 2 }; case 9: return { width: 1, height: 1, length: 2 }; case 11: switch (format) { case 5: return { width: 1, height: 1, length: 4 }; case 11: return { width: 1, height: 1, length: 4 }; default: return { width: 1, height: 1, length: 4 }; } } return { width: 1, height: 1, length: 4 }; } } FrameGraphTextureManager._Counter = 2; // 0 and 1 are reserved for backbuffer textures //# sourceMappingURL=frameGraphTextureManager.js.map