import { nArray , assert , assertExists , spliceBisectRight , setBitFlagEnabled } from "../../util" ; import { clamp } from "../../MathHelpers" ; import { GfxMegaStateDescriptor , GfxInputState , GfxDevice , GfxRenderPass , GfxRenderPipelineDescriptor , GfxPrimitiveTopology , GfxBindingLayoutDescriptor , GfxBindingsDescriptor , GfxSamplerBinding , GfxProgram , GfxInputLayout , GfxFormat , GfxRenderPassDescriptor } from "../platform/GfxPlatform" ; import { defaultMegaState , copyMegaState , setMegaStateFlags } from "../helpers/GfxMegaStateDescriptorHelpers" ; import { GfxRenderCache } from "./GfxRenderCache" ; import { GfxRenderDynamicUniformBuffer } from "./GfxRenderDynamicUniformBuffer" ; export const enum GfxRendererLayer { BACKGROUND = 0x00 , ALPHA_TEST = 0x10 , OPAQUE = 0x20 , TRANSLUCENT = 0x80 } const MAX_DEPTH = 0x10000 ; const DEPTH_BITS = 16 ; export function makeDepthKey ( depth , flipDepth , maxDepth = MAX_DEPTH ) { let normalizedDepth = ( clamp ( depth , 0 , maxDepth ) / maxDepth ) ; if ( flipDepth ) normalizedDepth = 1.0 - normalizedDepth ; const depthKey = ( normalizedDepth * ( ( 1 << DEPTH_BITS ) - 1 ) ) ; return depthKey & 0xFFFF ; } export function getSortKeyLayer ( sortKey ) { return ( sortKey >>> 24 ) & 0xFF ; } export function setSortKeyLayer ( sortKey , layer ) { return ( ( sortKey & 0x00FFFFFF ) | ( ( layer & 0xFF ) << 24 ) ) >>> 0 ; } export function setSortKeyProgramKey ( sortKey , programKey ) { const isTransparent = ! ! ( ( sortKey >>> 31 ) & 1 ) ; if ( isTransparent ) return sortKey ; else return ( ( sortKey & 0xFF0000FF ) | ( ( programKey & 0xFFFF ) << 8 ) ) >>> 0 ; } export function setSortKeyBias ( sortKey , bias ) { const isTransparent = ! ! ( ( sortKey >>> 31 ) & 1 ) ; if ( isTransparent ) return ( ( sortKey & 0xFFFFFF00 ) | ( bias & 0xFF ) ) >>> 0 ; else return sortKey ; } export function makeSortKeyOpaque ( layer , programKey ) { return setSortKeyLayer ( setSortKeyProgramKey ( 0 , programKey ) , layer ) ; } export function setSortKeyOpaqueDepth ( sortKey , depthKey ) { assert ( depthKey >= 0 ) ; return ( ( sortKey & 0xFFFFFF00 ) | ( ( depthKey >>> 8 ) & 0xFF ) ) >>> 0 ; } export function makeSortKeyTranslucent ( layer ) { return setSortKeyLayer ( 0 , layer ) ; } export function setSortKeyTranslucentDepth ( sortKey , depthKey ) { assert ( depthKey >= 0 ) ; return ( ( sortKey & 0xFF0000FF ) | ( depthKey << 8 ) ) >>> 0 ; } export function makeSortKey ( layer , programKey = 0 ) { if ( layer & GfxRendererLayer . TRANSLUCENT ) return makeSortKeyTranslucent ( layer ) ; else return makeSortKeyOpaque ( layer , programKey ) ; } export function setSortKeyDepthKey ( sortKey , depthKey ) { const isTranslucent = ! ! ( ( sortKey >>> 31 ) & 1 ) ; return isTranslucent ? setSortKeyTranslucentDepth ( sortKey , depthKey ) : setSortKeyOpaqueDepth ( sortKey , depthKey ) ; } export function setSortKeyDepth ( sortKey , depth , maxDepth = MAX_DEPTH ) { const isTranslucent = ! ! ( ( sortKey >>> 31 ) & 1 ) ; const depthKey = makeDepthKey ( depth , isTranslucent , maxDepth ) ; return isTranslucent ? setSortKeyTranslucentDepth ( sortKey , depthKey ) : setSortKeyOpaqueDepth ( sortKey , depthKey ) ; } export function getSortKeyDepth ( sortKey ) { const isTranslucent = ! ! ( ( sortKey >>> 31 ) & 1 ) ; if ( isTranslucent ) return ( sortKey >>> 8 ) & 0xFFFF ; else { return ( ( sortKey >>> 8 ) & 0xFFFC | ( sortKey & 0x03 ) ) ; } } const enum GfxRenderInstFlags { Indexed = 1 << 0 , AllowSkippingIfPipelineNotReady = 1 << 1 , Template = 1 << 2 , Draw = 1 << 3 , InheritedFlags = Indexed | AllowSkippingIfPipelineNotReady } export class GfxRenderInst { public sortKey : number = 0 ; public filterKey : number = 0 ; public debug : any = null ; private _renderPipelineDescriptor : GfxRenderPipelineDescriptor ; private _uniformBuffer : GfxRenderDynamicUniformBuffer ; private _bindingDescriptors : GfxBindingsDescriptor [ ] = nArray ( 1 , ( ) => ( { bindingLayout : null ! , samplerBindings : [ ] , uniformBufferBindings : [ ] } ) ) ; private _dynamicUniformBufferByteOffsets : number [ ] = nArray ( 4 , ( ) => 0 ) ; public _flags : GfxRenderInstFlags = 0 ; private _inputState : GfxInputState | null = null ; private _drawStart : number = 0 ; private _drawCount : number = 0 ; private _drawInstanceCount : number = 0 ; constructor ( ) { this . _renderPipelineDescriptor = { bindingLayouts : [ ] , inputLayout : null , megaStateDescriptor : copyMegaState ( defaultMegaState ) , program : null ! , topology : GfxPrimitiveTopology . Triangles , colorAttachmentFormats : [ ] , depthStencilAttachmentFormat : null , sampleCount : 1 , } ; this . reset ( ) ; } /**
     * Resets a render inst to be boring, so it can re-enter the pool.
     * Normally, you should not need to call this.
     *
     * {@private}
     */ public reset ( ) { this . sortKey = 0 ; this . filterKey = 0 ; this . _flags = GfxRenderInstFlags . AllowSkippingIfPipelineNotReady ; this . _inputState = null ; this . _renderPipelineDescriptor . inputLayout = null ; } /**
     * Copies the fields from another render inst {@param o} to this render inst.
     * Normally, you should not need to call this.
     *
     * {@private}
     */ public setFromTemplate ( o ) { setMegaStateFlags ( this . _renderPipelineDescriptor . megaStateDescriptor , o . _renderPipelineDescriptor . megaStateDescriptor ) ; this . _renderPipelineDescriptor . program = o . _renderPipelineDescriptor . program ; this . _renderPipelineDescriptor . inputLayout = o . _renderPipelineDescriptor . inputLayout ; this . _renderPipelineDescriptor . topology = o . _renderPipelineDescriptor . topology ; this . _renderPipelineDescriptor . colorAttachmentFormats . length = Math . max ( this . _renderPipelineDescriptor . colorAttachmentFormats . length , o . _renderPipelineDescriptor . colorAttachmentFormats . length ) ; for ( let i = 0 ; i < o . _renderPipelineDescriptor . colorAttachmentFormats . length ; i ++ ) this . _renderPipelineDescriptor . colorAttachmentFormats [ i ] = o . _renderPipelineDescriptor . colorAttachmentFormats [ i ] ; this . _renderPipelineDescriptor . depthStencilAttachmentFormat = o . _renderPipelineDescriptor . depthStencilAttachmentFormat ; this . _renderPipelineDescriptor . sampleCount = o . _renderPipelineDescriptor . sampleCount ; this . _inputState = o . _inputState ; this . _uniformBuffer = o . _uniformBuffer ; this . _drawCount = o . _drawCount ; this . _drawStart = o . _drawStart ; this . _drawInstanceCount = o . _drawInstanceCount ; this . _flags = ( this . _flags & ~ GfxRenderInstFlags . InheritedFlags ) | ( o . _flags & GfxRenderInstFlags . InheritedFlags ) ; this . sortKey = o . sortKey ; this . filterKey = o . filterKey ; const tbd = this . _bindingDescriptors [ 0 ] , obd = o . _bindingDescriptors [ 0 ] ; if ( obd . bindingLayout !== null ) this . _setBindingLayout ( obd . bindingLayout ) ; for ( let i = 0 ; i < Math . min ( tbd . uniformBufferBindings . length , obd . uniformBufferBindings . length ) ; i ++ ) tbd . uniformBufferBindings [ i ] . wordCount = o . _bindingDescriptors [ 0 ] . uniformBufferBindings [ i ] . wordCount ; this . setSamplerBindingsFromTextureMappings ( obd . samplerBindings ) ; for ( let i = 0 ; i < o . _dynamicUniformBufferByteOffsets . length ; i ++ ) this . _dynamicUniformBufferByteOffsets [ i ] = o . _dynamicUniformBufferByteOffsets [ i ] ; } public validate ( ) { for ( let i = 0 ; i < this . _bindingDescriptors . length ; i ++ ) { const bd = this . _bindingDescriptors [ i ] ; for ( let j = 0 ; j < bd . bindingLayout . numUniformBuffers ; j ++ ) assert ( bd . uniformBufferBindings [ j ] . wordCount > 0 ) ; } } /**
     * Set the {@see GfxProgram} that this render inst will render with. This is part of the automatic
     * pipeline building facilities. At render time, a pipeline will be automatically and constructed from
     * the pipeline parameters.
     */ public setGfxProgram ( program ) { this . _renderPipelineDescriptor . program = program ; } /**
     * Set the {@see GfxMegaStateDescriptor} that this render inst will render with. This is part of the automatic
     * pipeline building facilities. At render time, a pipeline will be automatically and constructed from
     * the pipeline parameters.
     */ public setMegaStateFlags ( r ) { setMegaStateFlags ( this . _renderPipelineDescriptor . megaStateDescriptor , r ) ; return this . _renderPipelineDescriptor . megaStateDescriptor ; } /**
     * Retrieve the {@see GfxMegaStateDescriptor} property bag that this will render with. This is similar to
     * {@see setMegaStateFlags} but allows you to set fields directly on the internal property bag, rather than
     * merge them. This can be slightly more efficient.
     */ public getMegaStateFlags ( ) { return this . _renderPipelineDescriptor . megaStateDescriptor ; } /**
     * Sets both the {@see GfxInputLayout} and {@see GfxInputState} to be used by this render instance.
     * The {@see GfxInputLayout} is used to construct the pipeline as part of the automatic pipeline building
     * facilities, while {@see GfxInputState} is used for the render.
     */ public setInputLayoutAndState ( inputLayout , inputState ) { this . _inputState = inputState ; this . _renderPipelineDescriptor . inputLayout = inputLayout ; } private _setBindingLayout ( bindingLayout ) { assert ( bindingLayout . numUniformBuffers <= this . _dynamicUniformBufferByteOffsets . length ) ; this . _renderPipelineDescriptor . bindingLayouts [ 0 ] = bindingLayout ; this . _bindingDescriptors [ 0 ] . bindingLayout = bindingLayout ; for ( let i = this . _bindingDescriptors [ 0 ] . uniformBufferBindings . length ; i < bindingLayout . numUniformBuffers ; i ++ ) this . _bindingDescriptors [ 0 ] . uniformBufferBindings . push ( { buffer : null ! , wordCount : 0 } ) ; for ( let i = this . _bindingDescriptors [ 0 ] . samplerBindings . length ; i < bindingLayout . numSamplers ; i ++ ) this . _bindingDescriptors [ 0 ] . samplerBindings . push ( { gfxSampler : null , gfxTexture : null , lateBinding : null } ) ; } /**
     * Sets the {@see GfxBindingLayoutDescriptor}s that this render inst will render with.
     */ public setBindingLayouts ( bindingLayouts ) { assert ( bindingLayouts . length <= this . _bindingDescriptors . length ) ; assert ( bindingLayouts . length === 1 ) ; this . _setBindingLayout ( bindingLayouts [ 0 ] ) ; } public drawIndexes ( indexCount , indexStart = 0 ) { this . _flags = setBitFlagEnabled ( this . _flags , GfxRenderInstFlags . Indexed , true ) ; this . _drawCount = indexCount ; this . _drawStart = indexStart ; this . _drawInstanceCount = 1 ; } public drawIndexesInstanced ( indexCount , instanceCount , indexStart = 0 ) { this . _flags = setBitFlagEnabled ( this . _flags , GfxRenderInstFlags . Indexed , true ) ; this . _drawCount = indexCount ; this . _drawStart = indexStart ; this . _drawInstanceCount = instanceCount ; } public drawPrimitives ( primitiveCount , primitiveStart = 0 ) { this . _flags = setBitFlagEnabled ( this . _flags , GfxRenderInstFlags . Indexed , false ) ; this . _drawCount = primitiveCount ; this . _drawStart = primitiveStart ; this . _drawInstanceCount = 1 ; } public setUniformBuffer ( uniformBuffer ) { this . _uniformBuffer = uniformBuffer ; } /**
     * Allocates {@param wordCount} words from the uniform buffer and assigns it to the buffer
     * slot at index {@param bufferIndex}. As a convenience, this also directly returns the same
     * offset into the uniform buffer, in words, that would be returned by a subsequent call to
     * {@see getUniformBufferOffset}.
     */ public allocateUniformBuffer ( bufferIndex , wordCount ) { assert ( this . _bindingDescriptors [ 0 ] . bindingLayout . numUniformBuffers <= this . _dynamicUniformBufferByteOffsets . length ) ; this . _dynamicUniformBufferByteOffsets [ bufferIndex ] = this . _uniformBuffer . allocateChunk ( wordCount ) << 2 ; const dst = this . _bindingDescriptors [ 0 ] . uniformBufferBindings [ bufferIndex ] ; dst . wordCount = wordCount ; return this . getUniformBufferOffset ( bufferIndex ) ; } /**
     * Returns the offset into the uniform buffer, in words, that is assigned to the buffer slot
     * at index {@param bufferIndex}, to be used with e.g. {@see mapUniformBufferF32}.
     */ public getUniformBufferOffset ( bufferIndex ) { const wordOffset = this . _dynamicUniformBufferByteOffsets [ bufferIndex ] >>> 2 ; return wordOffset ; } /**
     * Directly sets the uniform buffer assigned to the buffer slot at index {@param bufferIndex}
     * to be {@param wordOffset}. Use this if you have already allocated a uniform buffer chunk through
     * some other means and wish to directly assign it to this render inst.
     */ public setUniformBufferOffset ( bufferIndex , wordOffset , wordCount ) { this . _dynamicUniformBufferByteOffsets [ bufferIndex ] = wordOffset << 2 ; const dst = this . _bindingDescriptors [ 0 ] . uniformBufferBindings [ bufferIndex ] ; dst . wordCount = wordCount ; } /**
     * This is a convenience wrapper for {@see GfxRenderDynamicUniformBuffer.mapBufferF32}, but uses
     * the values previously assigned for the uniform buffer slot at index {@param bufferIndex}.
     * Like {@see GfxRenderDynamicUniformBuffer.mapBufferF32}, this does not return a slice for the
     * buffer; you need to write to it with the correct uniform buffer offset; this will usually be
     * returned by {@see allocateUniformBuffer}.
     */ public mapUniformBufferF32 ( bufferIndex ) { return this . _uniformBuffer . mapBufferF32 ( ) ; } /**
     * Retrieve the {@see GfxRenderDynamicUniformBuffer} that this render inst will use to allocate.
     */ public getUniformBuffer ( ) { return this . _uniformBuffer ; } /**
     * Sets the {@param GfxSamplerBinding}s in use by this render instance.
     *
     * Note that {@see GfxRenderInst} has a method of doing late binding, intended to solve cases where live render
     * targets are used, which can have difficult control flow consequences for users. Pass a string instead of a
     * GfxSamplerBinding to record that it can be resolved later, and use {@see GfxRenderInst.resolveLateSamplerBinding}
     * or equivalent to fill it in later.
     */ public setSamplerBindingsFromTextureMappings ( m ) { for ( let i = 0 ; i < this . _bindingDescriptors [ 0 ] . samplerBindings . length ; i ++ ) { const dst = this . _bindingDescriptors [ 0 ] . samplerBindings [ i ] ; const binding = m [ i ] ; if ( binding === undefined || binding === null ) { dst . gfxTexture = null ; dst . gfxSampler = null ; dst . lateBinding = null ; continue ; } dst . gfxTexture = binding . gfxTexture ; dst . gfxSampler = binding . gfxSampler ; dst . lateBinding = binding . lateBinding ; } } public hasLateSamplerBinding ( name ) { for ( let i = 0 ; i < this . _bindingDescriptors [ 0 ] . samplerBindings . length ; i ++ ) { const dst = this . _bindingDescriptors [ 0 ] . samplerBindings [ i ] ; if ( dst . lateBinding === name ) return true ; } return false ; } /**
     * Resolve a previously registered "late bound" sampler binding for the given {@param name} to the provided
     * {@param binding}, as registered through {@see setSamplerBindingsFromTextureMappings}.
     *
     * This is intended to be called by high-level code, and is especially helpful when juggling render targets
     * for framebuffer effects.
     */ public resolveLateSamplerBinding ( name , binding ) { for ( let i = 0 ; i < this . _bindingDescriptors [ 0 ] . samplerBindings . length ; i ++ ) { const dst = this . _bindingDescriptors [ 0 ] . samplerBindings [ i ] ; if ( dst . lateBinding === name ) { if ( binding === null ) { dst . gfxTexture = null ; dst . gfxSampler = null ; } else { assert ( binding . lateBinding === null ) ; dst . gfxTexture = binding . gfxTexture ; if ( binding . gfxSampler !== null ) dst . gfxSampler = binding . gfxSampler ; } dst . lateBinding = null ; } } } /**
     * Sets whether this render inst should be skipped if the render pipeline isn't ready.
     *
     * Some draws of objects can be skipped if the pipelines aren't ready. Others are more
     * crucial to draw, and so this can be set to force for the pipeline to become available.
     *
     * By default, this is true.
     */ public setAllowSkippingIfPipelineNotReady ( v ) { this . _flags = setBitFlagEnabled ( this . _flags , GfxRenderInstFlags . AllowSkippingIfPipelineNotReady , v ) ; } private setAttachmentFormatsFromRenderPass ( device , passRenderer ) { const passDescriptor = device . queryRenderPass ( passRenderer ) ; let sampleCount = - 1 ; for ( let i = 0 ; i < passDescriptor . colorAttachment . length ; i ++ ) { const colorAttachmentDescriptor = passDescriptor . colorAttachment [ i ] !== null ? device . queryRenderTarget ( passDescriptor . colorAttachment [ i ] ! ) : null ; this . _renderPipelineDescriptor . colorAttachmentFormats [ i ] = colorAttachmentDescriptor !== null ? colorAttachmentDescriptor . pixelFormat : null ; if ( colorAttachmentDescriptor !== null ) { if ( sampleCount === - 1 ) sampleCount = colorAttachmentDescriptor . sampleCount ; else assert ( sampleCount === colorAttachmentDescriptor . sampleCount ) ; } } const depthStencilAttachmentDescriptor = passDescriptor . depthStencilAttachment !== null ? device . queryRenderTarget ( passDescriptor . depthStencilAttachment ) : null ; this . _renderPipelineDescriptor . depthStencilAttachmentFormat = depthStencilAttachmentDescriptor !== null ? depthStencilAttachmentDescriptor . pixelFormat : null ; if ( depthStencilAttachmentDescriptor !== null ) { if ( sampleCount === - 1 ) sampleCount = depthStencilAttachmentDescriptor . sampleCount ; else assert ( sampleCount == depthStencilAttachmentDescriptor . sampleCount ) ; } assert ( sampleCount > 0 ) ; this . _renderPipelineDescriptor . sampleCount = sampleCount ; } public drawOnPass ( cache , passRenderer ) { const device = cache . device ; this . setAttachmentFormatsFromRenderPass ( device , passRenderer ) ; const gfxPipeline = cache . createRenderPipeline ( this . _renderPipelineDescriptor ) ; const pipelineReady = device . pipelineQueryReady ( gfxPipeline ) ; if ( ! pipelineReady ) { if ( ! ! ( this . _flags & GfxRenderInstFlags . AllowSkippingIfPipelineNotReady ) ) return false ; device . pipelineForceReady ( gfxPipeline ) ; } passRenderer . setPipeline ( gfxPipeline ) ; passRenderer . setInputState ( this . _inputState ) ; for ( let i = 0 ; i < this . _bindingDescriptors [ 0 ] . uniformBufferBindings . length ; i ++ ) this . _bindingDescriptors [ 0 ] . uniformBufferBindings [ i ] . buffer = assertExists ( this . _uniformBuffer . gfxBuffer ) ; const gfxBindings = cache . createBindings ( this . _bindingDescriptors [ 0 ] ) ; passRenderer . setBindings ( 0 , gfxBindings , this . _dynamicUniformBufferByteOffsets ) ; if ( this . _drawInstanceCount > 1 ) { assert ( ! ! ( this . _flags & GfxRenderInstFlags . Indexed ) ) ; passRenderer . drawIndexedInstanced ( this . _drawCount , this . _drawStart , this . _drawInstanceCount ) ; } else if ( ( this . _flags & GfxRenderInstFlags . Indexed ) ) { passRenderer . drawIndexed ( this . _drawCount , this . _drawStart ) ; } else { passRenderer . draw ( this . _drawCount , this . _drawStart ) ; } return true ; } } export const gfxRenderInstCompareNone = null ; export function gfxRenderInstCompareSortKey ( a , b ) { return a . sortKey - b . sortKey ; } export const enum GfxRenderInstExecutionOrder { Forwards , Backwards } export type GfxRenderInstCompareFunc = ( a : GfxRenderInst , b : GfxRenderInst ) => number ; export class GfxRenderInstList { public renderInsts : GfxRenderInst [ ] = [ ] ; private usePostSort : boolean = false ; constructor ( public compareFunction = gfxRenderInstCompareSortKey , public executionOrder = GfxRenderInstExecutionOrder . Forwards ) { } /**
     * Determine whether to use post-sorting, based on some heuristics.
     */ public checkUsePostSort ( ) { this . usePostSort = this . compareFunction !== null && this . renderInsts . length >= 500 ; } /**
     * Insert a render inst to the list. This directly inserts the render inst to
     * the position specified by the compare function, so the render inst must be
     * fully constructed at this point.
     */ private insertSorted ( renderInst ) { if ( this . compareFunction === null ) { this . renderInsts . push ( renderInst ) ; } else if ( this . usePostSort ) { this . renderInsts . push ( renderInst ) ; } else { spliceBisectRight ( this . renderInsts , renderInst , this . compareFunction ) ; } this . checkUsePostSort ( ) ; } public submitRenderInst ( renderInst ) { renderInst . validate ( ) ; renderInst . _flags |= GfxRenderInstFlags . Draw ; this . insertSorted ( renderInst ) ; } public hasLateSamplerBinding ( name ) { for ( let i = 0 ; i < this . renderInsts . length ; i ++ ) if ( this . renderInsts [ i ] . hasLateSamplerBinding ( name ) ) return true ; return false ; } /**
     * Resolve sampler bindings for all render insts on this render inst list. See the
     * documentation for {@see GfxRenderInst.resolveLateSamplerBinding}.
     */ public resolveLateSamplerBinding ( name , binding ) { for ( let i = 0 ; i < this . renderInsts . length ; i ++ ) this . renderInsts [ i ] . resolveLateSamplerBinding ( name , binding ) ; } public ensureSorted ( ) { if ( this . usePostSort ) { if ( this . renderInsts . length !== 0 ) this . renderInsts . sort ( this . compareFunction ! ) ; this . usePostSort = false ; } } private drawOnPassRendererNoReset ( cache , passRenderer ) { this . ensureSorted ( ) ; let numDrawn = 0 ; if ( this . executionOrder === GfxRenderInstExecutionOrder . Forwards ) { for ( let i = 0 ; i < this . renderInsts . length ; i ++ ) if ( this . renderInsts [ i ] . drawOnPass ( cache , passRenderer ) ) numDrawn ++ ; } else { for ( let i = this . renderInsts . length - 1 ; i >= 0 ; i -- ) if ( this . renderInsts [ i ] . drawOnPass ( cache , passRenderer ) ) numDrawn ++ ; } return numDrawn ; } public reset ( ) { this . renderInsts . length = 0 ; } /**
     * Execute all scheduled render insts in this list onto the {@param GfxRenderPass},
     * using {@param device} and {@param cache} to create any device-specific resources
     * necessary to complete the draws.
     */ public drawOnPassRenderer ( cache , passRenderer ) { const numDrawn = this . drawOnPassRendererNoReset ( cache , passRenderer ) ; this . reset ( ) ; return numDrawn ; } } class RenderInstPool { public pool : GfxRenderInst [ ] = [ ] ; public allocCount : number = 0 ; public allocRenderInstIndex ( ) { this . allocCount ++ ; if ( this . allocCount > this . pool . length ) this . pool . push ( new GfxRenderInst ( ) ) ; return this . allocCount - 1 ; } public popRenderInst ( ) { this . allocCount -- ; } public reset ( ) { for ( let i = 0 ; i < this . pool . length ; i ++ ) this . pool [ i ] . reset ( ) ; this . allocCount = 0 ; } public destroy ( ) { this . pool . length = 0 ; this . allocCount = 0 ; } } export class GfxRenderInstManager { public instPool = new RenderInstPool ( ) ; public templatePool = new RenderInstPool ( ) ; public simpleRenderInstList : GfxRenderInstList | null = new GfxRenderInstList ( ) ; public currentRenderInstList : GfxRenderInstList = this . simpleRenderInstList ! ; constructor ( public gfxRenderCache ) { } /**
     * Creates a new {@see GfxRenderInst} object and returns it. If there is a template
     * pushed onto the template stack, then its values will be used as a base for this
     * render inst.
     */ public newRenderInst ( ) { const templateIndex = this . templatePool . allocCount - 1 ; const renderInstIndex = this . instPool . allocRenderInstIndex ( ) ; const renderInst = this . instPool . pool [ renderInstIndex ] ; renderInst . debug = null ; if ( templateIndex >= 0 ) renderInst . setFromTemplate ( this . templatePool . pool [ templateIndex ] ) ; return renderInst ; } /**
     * Submits {@param renderInst} to the current render inst list. Note that
     * this assumes the render inst was fully filled in, so do not modify it
     * after submitting it.
     */ public submitRenderInst ( renderInst , list = this . currentRenderInstList ) { list . submitRenderInst ( renderInst ) ; } /**
     * Sets the currently active render inst list. This is the list that will
     * be used by @param submitRenderInst}. If you use this function, please
     * make sure to call {@see disableSimpleMode} when the GfxRenderInstManager
     * is created, to ensure that nobody uses the "legacy" APIs. Failure to do
     * so might cause memory leaks or other problems.
     */ public setCurrentRenderInstList ( list ) { assert ( this . simpleRenderInstList === null ) ; this . currentRenderInstList = list ; } /**
     * Pushes a new template render inst to the template stack. All properties set
     * on the topmost template on the template stack will be the defaults for both
     * for any future render insts created. Once done with a template, call
     * {@param popTemplateRenderInst} to pop it off the template stack.
     */ public pushTemplateRenderInst ( ) { const templateIndex = this . templatePool . allocCount - 1 ; const newTemplateIndex = this . templatePool . allocRenderInstIndex ( ) ; const newTemplate = this . templatePool . pool [ newTemplateIndex ] ; if ( templateIndex >= 0 ) newTemplate . setFromTemplate ( this . templatePool . pool [ templateIndex ] ) ; newTemplate . _flags |= GfxRenderInstFlags . Template ; return newTemplate ; } public popTemplateRenderInst ( ) { this . templatePool . popRenderInst ( ) ; } /**
     * Retrieves the current template render inst on the top of the template stack.
     */ public getTemplateRenderInst ( ) { const templateIndex = this . templatePool . allocCount - 1 ; return this . templatePool . pool [ templateIndex ] ; } /**
     * Reset all allocated render insts. This should be called at the end of the frame,
     * once done with all of the allocated render insts and render inst lists.
     */ public resetRenderInsts ( ) { this . instPool . reset ( ) ; if ( this . simpleRenderInstList !== null ) this . simpleRenderInstList . reset ( ) ; assert ( this . templatePool . allocCount === 0 ) ; } public destroy ( ) { this . instPool . destroy ( ) ; } /**
     * Disables the "simple" render inst list management API.
     */ public disableSimpleMode ( ) { this . simpleRenderInstList = null ; } /**
     * {@deprecated}
     */ public setVisibleByFilterKeyExact ( filterKey ) { const list = assertExists ( this . simpleRenderInstList ) ; list . checkUsePostSort ( ) ; list . renderInsts . length = 0 ; for ( let i = 0 ; i < this . instPool . allocCount ; i ++ ) if ( ! ! ( this . instPool . pool [ i ] . _flags & GfxRenderInstFlags . Draw ) && this . instPool . pool [ i ] . filterKey === filterKey ) list . submitRenderInst ( this . instPool . pool [ i ] ) ; } /**
     * {@deprecated}
     */ public hasAnyVisible ( ) { const list = assertExists ( this . simpleRenderInstList ) ; return list . renderInsts . length > 0 ; } /**
     * {@deprecated}
     */ public setVisibleNone ( ) { const list = assertExists ( this . simpleRenderInstList ) ; list . renderInsts . length = 0 ; } public drawOnPassRenderer ( passRenderer ) { const list = assertExists ( this . simpleRenderInstList ) ; list . drawOnPassRenderer ( this . gfxRenderCache , passRenderer ) ; } } /**
 * {@deprecated}
 */ export function executeOnPass ( renderInstManager , passRenderer , passMask ) { renderInstManager . setVisibleByFilterKeyExact ( passMask ) ; renderInstManager . drawOnPassRenderer ( passRenderer ) ; } /**
 * {@deprecated}
 */ export function hasAnyVisible ( renderInstManager , passMask ) { renderInstManager . setVisibleByFilterKeyExact ( passMask ) ; return renderInstManager . hasAnyVisible ( ) ; }