import { GfxColor , GfxRenderTarget , GfxDevice , GfxFormat , GfxNormalizedViewportCoords , GfxRenderPass , GfxRenderPassDescriptor , GfxTexture , GfxTextureDimension , GfxTextureUsage } from "../platform/GfxPlatform" ; import { GfxQueryPool } from "../platform/GfxPlatformImpl" ; import { assert , assertExists } from "../platform/GfxPlatformUtil" ; export class GfxrRenderTargetDescription { public width : number = 0 ; public height : number = 0 ; public sampleCount : number = 0 ; public colorClearColor : Readonly < GfxColor > | 'load' = 'load' ; public depthClearValue : number | 'load' = 'load' ; public stencilClearValue : number | 'load' = 'load' ; constructor ( public pixelFormat ) { } /**
     * Set the dimensions of a render target description.
     */ public setDimensions ( width , height , sampleCount ) { this . width = width ; this . height = height ; this . sampleCount = sampleCount ; } public copyDimensions ( desc ) { this . width = desc . width ; this . height = desc . height ; this . sampleCount = desc . sampleCount ; } } export const enum GfxrAttachmentSlot { Color0 = 0 , Color1 = 1 , Color2 = 2 , Color3 = 3 , ColorMax = Color3 , DepthStencil } export const IdentityViewportCoords = { x : 0 , y : 0 , w : 1 , h : 1 } ; type PassExecFunc = ( passRenderer : GfxRenderPass , scope : GfxrPassScope ) => void ; type PassPostFunc = ( scope : GfxrPassScope ) => void ; declare const isRenderTargetID ; export type GfxrRenderTargetID = number & { [ isRenderTargetID ] : true ; } ; declare const isResolveTextureID ; export type GfxrResolveTextureID = number & { [ isResolveTextureID ] : true ; } ; export interface GfxrPass { /**
     * Set the debug name of a given pass. Strongly encouraged.
     */ setDebugName ( debugName ) : void ; /**
     * Call when you want to output a debug thumbnail.
     */ pushDebugThumbnail ( attachmentSlot ) : void ; /**
     * Attach the given render target with ID {@param renderTargetID} to the given attachment slot.
     *
     * This determines which render targets this pass will render to.
     */ attachRenderTargetID ( attachmentSlot , renderTargetID ) : void ; /**
     * Attach the occlusion query pool used by this rendering pass.
     */ attachOcclusionQueryPool ( queryPool ) : void ; /**
     * Set the viewport used by this rendering pass.
     */ setViewport ( viewport ) : void ; /**
     * Attach the resolve texture ID to the given pass. All resolve textures used within the pass
     * must be attached before-hand in order for the scheduler to properly allocate our resolve texture.
     */ attachResolveTexture ( resolveTextureID ) : void ; /**
     * Set the pass's execution callback. This will be called with the {@see GfxRenderPass} for the
     * pass, along with the {@see GfxrPassScope} to access any resources that the system has allocated.
     */ exec ( func ) : void ; /**
     * Set the pass's post callback. This will be immediately right after the pass is submitted,
     * allowing you to do additional custom work once the pass has been done. This is expected to be
     * seldomly used.
     */ post ( func ) : void ; addExtraRef ( renderTargetID ) : void ; } class PassImpl implements GfxrPass { public debugName : string = '' ; public renderTargetIDs : GfxrRenderTargetID [ ] = [ ] ; public resolveTextureOutputIDs : GfxrResolveTextureID [ ] = [ ] ; public resolveTextureOutputExternalTextures : GfxTexture [ ] = [ ] ; public resolveTextureInputIDs : GfxrResolveTextureID [ ] = [ ] ; public renderTargetExtraRefs : boolean [ ] = [ ] ; public viewport : GfxNormalizedViewportCoords = IdentityViewportCoords ; public resolveTextureInputTextures : GfxTexture [ ] = [ ] ; public renderTargets : ( RenderTarget | null ) [ ] = [ ] ; public descriptor : GfxRenderPassDescriptor = { colorAttachment : [ ] , colorResolveTo : [ ] , colorStore : [ ] , depthStencilAttachment : null , depthStencilResolveTo : null , depthStencilStore : true , colorClearColor : [ 'load' ] , depthClearValue : 'load' , stencilClearValue : 'load' , occlusionQueryPool : null , } ; public viewportX : number = 0 ; public viewportY : number = 0 ; public viewportW : number = 0 ; public viewportH : number = 0 ; public execFunc : PassExecFunc | null = null ; public postFunc : PassPostFunc | null = null ; public debugThumbnails : boolean [ ] = [ ] ; public setDebugName ( debugName ) { this . debugName = debugName ; } public pushDebugThumbnail ( attachmentSlot ) { this . debugThumbnails [ attachmentSlot ] = true ; } public attachRenderTargetID ( attachmentSlot , renderTargetID ) { assert ( this . renderTargetIDs [ attachmentSlot ] === undefined ) ; this . renderTargetIDs [ attachmentSlot ] = renderTargetID ; } public attachResolveTexture ( resolveTextureID ) { this . resolveTextureInputIDs . push ( resolveTextureID ) ; } public attachOcclusionQueryPool ( queryPool ) { this . descriptor . occlusionQueryPool = queryPool ; } public setViewport ( viewport ) { this . viewport = viewport ; } public resolveToExternalTexture ( attachmentSlot , texture ) { this . resolveTextureOutputExternalTextures [ attachmentSlot ] = texture ; } public exec ( func ) { assert ( this . execFunc === null ) ; this . execFunc = func ; } public post ( func ) { assert ( this . postFunc === null ) ; this . postFunc = func ; } public addExtraRef ( slot ) { this . renderTargetExtraRefs [ slot ] = true ; } } export interface GfxrPassScope { param id getResolveTextureForID ( id ) : GfxTexture ; /**
     * Retrieve the underlying render target resource for a given attachment slot {@param slot}.
     */ getRenderTargetAttachment ( slot ) : GfxRenderTarget | null ; /**
     * Retrieve the underlying texture resource for a given attachment slot {@param slot}. This is not
     * guaranteed to be a single-sampled texture; to resolve the resource, see {@see getResolveTextureForID}.
     */ getRenderTargetTexture ( slot ) : GfxTexture | null ; } class GraphImpl { [ Symbol . species ] ? : 'GfxrGraph' ; public renderTargetDescriptions : Readonly < GfxrRenderTargetDescription > [ ] = [ ] ; public resolveTextureRenderTargetIDs : number [ ] = [ ] ; public passes : PassImpl [ ] = [ ] ; public renderTargetDebugNames : string [ ] = [ ] ; } type PassSetupFunc = ( renderPass : GfxrPass ) => void ; export interface GfxrGraphBuilder { /**
     * Add a new pass. {@param setupFunc} will be called *immediately* to set up the
     * pass. This is wrapped in a function simply to limit the scope of a pass. It
     * is possible I might change this in the future to limit the allocations caused
     * by closures.
     */ pushPass ( setupFunc ) : void ; /**
     * Tell the system about a render target with the given descriptions. Render targets
     * are "virtual", and is only backed by an actual device resource when inside of a pass.
     * This allows render targets to be reused without the user having to track any of this
     * logic.
     *
     * When a pass has a render target ID attached, the created {@see GfxRenderPass} will have
     * the render targets already bound. To use a render target as an input to a rendering
     * algorithm, it must first be "resolved" to a texture. Use {@see resolveRenderTarget} to
     * get a resolved texture ID corresponding to a given render target.
     *
     * To retrieve actual backing resource for a given render target ID inside of a pass,
     * use the {@see GfxrPassScope} given to the pass's execution or post callbacks, however
     * this usage should be rarer than the resolve case.
     */ createRenderTargetID ( desc , debugName ) : GfxrRenderTargetID ; /**
     * Resolve the render target in slot {@param attachmentSlot} of pass {@param pass}, and return
     * the resolve texture ID.
     *
     * To bind the image of a render target in a rendering pass, it first must be "resolved" to
     * a texture. Please remember to attach the resolve texture to a pass where it is used with
     * {@see GfxrPassScope::attachResolveTexture}. When in the pass's execution or post callbacks,
     * you can retrieve a proper {@param GfxTexture} for a resolve texture ID with
     * {@see GfxrPassScope::getResolveTextureForID}}.
     */ resolveRenderTargetPassAttachmentSlot ( pass , attachmentSlot ) : GfxrResolveTextureID ; /**
     * Resolve the render target ID {@param renderTargetID}, and return the resolve texture ID.
     *
     * To bind the image of a render target in a rendering pass, it first must be "resolved" to
     * a texture. Please remember to attach the resolve texture to a pass where it is used with
     * {@see GfxrPassScope::attachResolveTexture}. When in the pass's execution or post callbacks,
     * you can retrieve a proper {@param GfxTexture} for a resolve texture ID with
     * {@see GfxrPassScope::getResolveTextureForID}}.
     *
     * This just looks up the last pass that drew to the render target {@param renderTargetID},
     * and then calls {@see resolveRenderTargetPassAttachmentSlot} using the information it found.
     */ resolveRenderTarget ( renderTargetID ) : GfxrResolveTextureID ; /**
     * Specify that the render target ID {@param renderTargetID} should be resolved to an
     * externally-provided texture. The texture must have been allocated by the user, and it must
     * match the dimensions of the render target.
     *
     * Warning: This API might change in the near future.
     */ resolveRenderTargetToExternalTexture ( renderTargetID , texture ) : void ; /**
     * Return the description that a render target was created with. This allows the creator to
     * not have to pass information to any dependent modules to derive from it.
     */ getRenderTargetDescription ( renderTargetID ) : Readonly < GfxrRenderTargetDescription > ; /**
     * Internal API.
     */ getDebug ( ) : GfxrGraphBuilderDebug ; } export interface GfxrGraphBuilderDebug { getPasses ( ) : GfxrPass [ ] ; getPassDebugThumbnails ( pass ) : boolean [ ] ; getPassRenderTargetID ( pass , slot ) : GfxrRenderTargetID ; getRenderTargetIDDebugName ( renderTargetID ) : string ; getPassDebugName ( pass ) : string ; } class RenderTarget { public debugName : string = '' ; public readonly dimension = GfxTextureDimension . n2D ; public readonly depth = 1 ; public readonly numLevels = 1 ; public pixelFormat : GfxFormat ; public width : number = 0 ; public height : number = 0 ; public sampleCount : number = 0 ; public usage : GfxTextureUsage = GfxTextureUsage . RenderTarget ; public needsClear : boolean = true ; public texture : GfxTexture | null = null ; public attachment : GfxRenderTarget ; public age : number = 0 ; constructor ( device , desc ) { this . pixelFormat = desc . pixelFormat ; this . width = desc . width ; this . height = desc . height ; this . sampleCount = desc . sampleCount ; assert ( this . sampleCount >= 1 ) ; if ( this . sampleCount > 1 ) { this . attachment = device . createRenderTarget ( this ) ; } else { this . texture = device . createTexture ( this ) ; this . attachment = device . createRenderTargetFromTexture ( this . texture ) ; } } public setDebugName ( device , debugName ) { this . debugName = debugName ; if ( this . texture !== null ) device . setResourceName ( this . texture , this . debugName ) ; device . setResourceName ( this . attachment , this . debugName ) ; } public matchesDescription ( desc ) { return this . pixelFormat === desc . pixelFormat && this . width === desc . width && this . height === desc . height && this . sampleCount === desc . sampleCount ; } public reset ( desc ) { assert ( this . matchesDescription ( desc ) ) ; this . age = 0 ; } public destroy ( device ) { if ( this . texture !== null ) device . destroyTexture ( this . texture ) ; device . destroyRenderTarget ( this . attachment ) ; } } class SingleSampledTexture { public readonly dimension = GfxTextureDimension . n2D ; public readonly depth = 1 ; public readonly numLevels = 1 ; public readonly usage = GfxTextureUsage . RenderTarget ; public pixelFormat : GfxFormat ; public width : number = 0 ; public height : number = 0 ; public texture : GfxTexture ; public age : number = 0 ; constructor ( device , desc ) { this . pixelFormat = desc . pixelFormat ; this . width = desc . width ; this . height = desc . height ; this . texture = device . createTexture ( this ) ; } public matchesDescription ( desc ) { return this . pixelFormat === desc . pixelFormat && this . width === desc . width && this . height === desc . height ; } public reset ( desc ) { assert ( this . matchesDescription ( desc ) ) ; this . age = 0 ; } public destroy ( device ) { device . destroyTexture ( this . texture ) ; } } export class GfxrTemporalTexture { private inputTexture : SingleSampledTexture | null = null ; private outputTexture : SingleSampledTexture | null = null ; public setDescription ( device , desc ) { if ( this . inputTexture !== this . outputTexture ) { if ( this . inputTexture !== null ) this . inputTexture . destroy ( device ) ; this . inputTexture = this . outputTexture ; } assert ( this . inputTexture === this . outputTexture ) ; if ( this . outputTexture !== null && this . outputTexture . matchesDescription ( desc ) ) return ; this . outputTexture = new SingleSampledTexture ( device , desc ) ; if ( this . inputTexture === null ) this . inputTexture = this . outputTexture ; } public getTextureForSampling ( ) { return this . inputTexture !== null ? this . inputTexture . texture : null ; } public getTextureForResolving ( ) { return assertExists ( this . outputTexture ) . texture ; } public destroy ( device ) { if ( this . outputTexture !== null && this . outputTexture !== this . inputTexture ) { this . outputTexture . destroy ( device ) ; this . outputTexture = null ; } if ( this . inputTexture !== null ) { this . inputTexture . destroy ( device ) ; this . inputTexture = null ; } } } function fillArray < T > ( L , n , v ) { L . length = n ; L . fill ( v ) ; } export interface GfxrRenderGraph { newGraphBuilder ( ) : GfxrGraphBuilder ; execute ( builder ) : void ; destroy ( ) : void ; } interface ResolveParam { resolveTo : GfxTexture | null ; store : boolean ; } export class GfxrRenderGraphImpl implements GfxrRenderGraph , GfxrGraphBuilder , GfxrRenderGraphImpl { private currentPass : PassImpl | null = null ; private renderTargetDeadPool : RenderTarget [ ] = [ ] ; private singleSampledTextureDeadPool : SingleSampledTexture [ ] = [ ] ; constructor ( private device ) { } private acquireRenderTargetForDescription ( desc ) { for ( let i = 0 ; i < this . renderTargetDeadPool . length ; i ++ ) { const freeRenderTarget = this . renderTargetDeadPool [ i ] ; if ( freeRenderTarget . matchesDescription ( desc ) ) { freeRenderTarget . reset ( desc ) ; this . renderTargetDeadPool . splice ( i -- , 1 ) ; return freeRenderTarget ; } } return new RenderTarget ( this . device , desc ) ; } private acquireSingleSampledTextureForDescription ( desc ) { for ( let i = 0 ; i < this . singleSampledTextureDeadPool . length ; i ++ ) { const freeSingleSampledTexture = this . singleSampledTextureDeadPool [ i ] ; if ( freeSingleSampledTexture . matchesDescription ( desc ) ) { freeSingleSampledTexture . reset ( desc ) ; this . singleSampledTextureDeadPool . splice ( i -- , 1 ) ; return freeSingleSampledTexture ; } } return new SingleSampledTexture ( this . device , desc ) ; } private currentGraph : GraphImpl | null = null ; public beginGraphBuilder ( ) { assert ( this . currentGraph === null ) ; this . currentGraph = new GraphImpl ( ) ; } public pushPass ( setupFunc ) { const pass = new PassImpl ( ) ; setupFunc ( pass ) ; this . currentGraph ! . passes . push ( pass ) ; } public createRenderTargetID ( desc , debugName ) { this . currentGraph ! . renderTargetDebugNames . push ( debugName ) ; return ( this . currentGraph ! . renderTargetDescriptions . push ( desc ) - 1 ) as GfxrRenderTargetID ; } private createResolveTextureID ( renderTargetID ) { return ( this . currentGraph ! . resolveTextureRenderTargetIDs . push ( renderTargetID ) - 1 ) as GfxrResolveTextureID ; } private findMostRecentPassThatAttachedRenderTarget ( renderTargetID ) { for ( let i = this . currentGraph ! . passes . length - 1 ; i >= 0 ; i -- ) { const pass = this . currentGraph ! . passes [ i ] ; if ( pass . renderTargetIDs . includes ( renderTargetID ) ) return pass ; } return null ; } private findPassForResolveRenderTarget ( renderTargetID ) { const renderPass = assertExists ( this . findMostRecentPassThatAttachedRenderTarget ( renderTargetID ) ) ; const attachmentSlot = renderPass . renderTargetIDs . indexOf ( renderTargetID ) ; assert ( renderPass . resolveTextureOutputExternalTextures [ attachmentSlot ] === undefined ) ; return renderPass ; } public resolveRenderTargetPassAttachmentSlot ( pass , attachmentSlot ) { const renderPass = pass as PassImpl ; if ( renderPass . resolveTextureOutputIDs [ attachmentSlot ] === undefined ) { const renderTargetID = renderPass . renderTargetIDs [ attachmentSlot ] ; const resolveTextureID = this . createResolveTextureID ( renderTargetID ) ; renderPass . resolveTextureOutputIDs [ attachmentSlot ] = resolveTextureID ; } return renderPass . resolveTextureOutputIDs [ attachmentSlot ] ; } public resolveRenderTarget ( renderTargetID ) { const renderPass = this . findPassForResolveRenderTarget ( renderTargetID ) ; const attachmentSlot = renderPass . renderTargetIDs . indexOf ( renderTargetID ) ; return this . resolveRenderTargetPassAttachmentSlot ( renderPass , attachmentSlot ) ; } public resolveRenderTargetToExternalTexture ( renderTargetID , texture ) { const renderPass = this . findPassForResolveRenderTarget ( renderTargetID ) ; const attachmentSlot = renderPass . renderTargetIDs . indexOf ( renderTargetID ) ; assert ( renderPass . resolveTextureOutputIDs [ attachmentSlot ] === undefined ) ; renderPass . resolveTextureOutputExternalTextures [ attachmentSlot ] = texture ; } public getRenderTargetDescription ( renderTargetID ) { return assertExists ( this . currentGraph ! . renderTargetDescriptions [ renderTargetID ] ) ; } private renderTargetOutputCount : number [ ] = [ ] ; private renderTargetResolveCount : number [ ] = [ ] ; private resolveTextureUseCount : number [ ] = [ ] ; private renderTargetAliveForID : RenderTarget [ ] = [ ] ; private singleSampledTextureForResolveTextureID : SingleSampledTexture [ ] = [ ] ; private scheduleAddUseCount ( graph , pass ) { for ( let slot = 0 ; slot < pass . renderTargetIDs . length ; slot ++ ) { const renderTargetID = pass . renderTargetIDs [ slot ] ; if ( renderTargetID === undefined ) continue ; this . renderTargetOutputCount [ renderTargetID ] ++ ; if ( pass . renderTargetExtraRefs [ slot ] ) this . renderTargetOutputCount [ renderTargetID ] ++ ; } for ( let i = 0 ; i < pass . resolveTextureInputIDs . length ; i ++ ) { const resolveTextureID = pass . resolveTextureInputIDs [ i ] ; if ( resolveTextureID === undefined ) continue ; this . resolveTextureUseCount [ resolveTextureID ] ++ ; const renderTargetID = graph . resolveTextureRenderTargetIDs [ resolveTextureID ] ; this . renderTargetResolveCount [ renderTargetID ] ++ ; } } private acquireRenderTargetForID ( graph , renderTargetID ) { if ( renderTargetID === undefined ) return null ; assert ( this . renderTargetOutputCount [ renderTargetID ] > 0 ) ; if ( ! this . renderTargetAliveForID [ renderTargetID ] ) { const desc = graph . renderTargetDescriptions [ renderTargetID ] ; const newRenderTarget = this . acquireRenderTargetForDescription ( desc ) ; newRenderTarget . setDebugName ( this . device , graph . renderTargetDebugNames [ renderTargetID ] ) ; this . renderTargetAliveForID [ renderTargetID ] = newRenderTarget ; } return this . renderTargetAliveForID [ renderTargetID ] ; } private releaseRenderTargetForID ( renderTargetID , forOutput ) { if ( renderTargetID === undefined ) return null ; const renderTarget = assertExists ( this . renderTargetAliveForID [ renderTargetID ] ) ; if ( forOutput ) { assert ( this . renderTargetOutputCount [ renderTargetID ] > 0 ) ; this . renderTargetOutputCount [ renderTargetID ] -- ; } else { assert ( this . renderTargetResolveCount [ renderTargetID ] > 0 ) ; this . renderTargetResolveCount [ renderTargetID ] -- ; } if ( this . renderTargetOutputCount [ renderTargetID ] === 0 && this . renderTargetResolveCount [ renderTargetID ] === 0 ) { renderTarget . needsClear = true ; delete this . renderTargetAliveForID [ renderTargetID ] ; this . renderTargetDeadPool . push ( renderTarget ) ; } return renderTarget ; } private acquireResolveTextureInputTextureForID ( graph , resolveTextureID ) { const renderTargetID = graph . resolveTextureRenderTargetIDs [ resolveTextureID ] ; assert ( this . resolveTextureUseCount [ resolveTextureID ] > 0 ) ; this . resolveTextureUseCount [ resolveTextureID ] -- ; const renderTarget = assertExists ( this . releaseRenderTargetForID ( renderTargetID , false ) ) ; if ( this . singleSampledTextureForResolveTextureID [ resolveTextureID ] !== undefined ) { const singleSampledTexture = this . singleSampledTextureForResolveTextureID [ resolveTextureID ] ; if ( this . resolveTextureUseCount [ resolveTextureID ] === 0 ) { this . singleSampledTextureDeadPool . push ( singleSampledTexture ) ; } return singleSampledTexture . texture ; } else { return assertExists ( renderTarget . texture ) ; } } private determineResolveParam ( graph , pass , slot ) { const renderTargetID = pass . renderTargetIDs [ slot ] ; const resolveTextureOutputID = pass . resolveTextureOutputIDs [ slot ] ; const externalTexture = pass . resolveTextureOutputExternalTextures [ slot ] ; const hasResolveTextureOutputID = resolveTextureOutputID !== undefined ; const hasExternalTexture = externalTexture !== undefined ; assert ( ! ( hasResolveTextureOutputID && hasExternalTexture ) ) ; let resolveTo = null ; let store = false ; if ( this . renderTargetOutputCount [ renderTargetID ] > 1 ) { store = true ; } if ( hasResolveTextureOutputID ) { assert ( graph . resolveTextureRenderTargetIDs [ resolveTextureOutputID ] === renderTargetID ) ; assert ( this . resolveTextureUseCount [ resolveTextureOutputID ] > 0 ) ; assert ( this . renderTargetOutputCount [ renderTargetID ] > 0 ) ; const renderTarget = assertExists ( this . renderTargetAliveForID [ renderTargetID ] ) ; if ( renderTarget . texture !== null && this . renderTargetOutputCount [ renderTargetID ] === 1 ) { resolveTo = null ; store = true ; } else { if ( ! this . singleSampledTextureForResolveTextureID [ resolveTextureOutputID ] ) { const desc = assertExists ( graph . renderTargetDescriptions [ renderTargetID ] ) ; this . singleSampledTextureForResolveTextureID [ resolveTextureOutputID ] = this . acquireSingleSampledTextureForDescription ( desc ) ; this . device . setResourceName ( this . singleSampledTextureForResolveTextureID [ resolveTextureOutputID ] . texture , renderTarget . debugName + ` (Resolve ${ resolveTextureOutputID })` ) ; } resolveTo = this . singleSampledTextureForResolveTextureID [ resolveTextureOutputID ] . texture ; } } else if ( hasExternalTexture ) { resolveTo = externalTexture ; } else { resolveTo = null ; } return { resolveTo , store } ; } private schedulePass ( graph , pass ) { const depthStencilRenderTargetID = pass . renderTargetIDs [ GfxrAttachmentSlot . DepthStencil ] ; for ( let slot = GfxrAttachmentSlot . Color0 ; slot <= GfxrAttachmentSlot . ColorMax ; slot ++ ) { const colorRenderTargetID = pass . renderTargetIDs [ slot ] ; const colorRenderTarget = this . acquireRenderTargetForID ( graph , colorRenderTargetID ) ; pass . renderTargets [ slot ] = colorRenderTarget ; pass . descriptor . colorAttachment [ slot ] = colorRenderTarget !== null ? colorRenderTarget . attachment : null ; const { resolveTo , store } = this . determineResolveParam ( graph , pass , slot ) ; pass . descriptor . colorResolveTo [ slot ] = resolveTo ; pass . descriptor . colorStore [ slot ] = store ; pass . descriptor . colorClearColor [ slot ] = ( colorRenderTarget !== null && colorRenderTarget . needsClear ) ? graph . renderTargetDescriptions [ colorRenderTargetID ] . colorClearColor : 'load' ; } const depthStencilRenderTarget = this . acquireRenderTargetForID ( graph , depthStencilRenderTargetID ) ; pass . renderTargets [ GfxrAttachmentSlot . DepthStencil ] = depthStencilRenderTarget ; pass . descriptor . depthStencilAttachment = depthStencilRenderTarget !== null ? depthStencilRenderTarget . attachment : null ; const { resolveTo , store } = this . determineResolveParam ( graph , pass , GfxrAttachmentSlot . DepthStencil ) ; pass . descriptor . depthStencilResolveTo = resolveTo ; pass . descriptor . depthStencilStore = store ; pass . descriptor . depthClearValue = ( depthStencilRenderTarget !== null && depthStencilRenderTarget . needsClear ) ? graph . renderTargetDescriptions [ depthStencilRenderTargetID ] . depthClearValue : 'load' ; pass . descriptor . stencilClearValue = ( depthStencilRenderTarget !== null && depthStencilRenderTarget . needsClear ) ? graph . renderTargetDescriptions [ depthStencilRenderTargetID ] . stencilClearValue : 'load' ; let rtWidth = 0 , rtHeight = 0 , rtSampleCount = 0 ; for ( let i = 0 ; i < pass . renderTargets . length ; i ++ ) { const renderTarget = pass . renderTargets [ i ] ; if ( ! renderTarget ) continue ; if ( rtWidth === 0 ) { rtWidth = renderTarget . width ; rtHeight = renderTarget . height ; rtSampleCount = renderTarget . sampleCount ; } assert ( renderTarget . width === rtWidth ) ; assert ( renderTarget . height === rtHeight ) ; assert ( renderTarget . sampleCount === rtSampleCount ) ; renderTarget . needsClear = false ; } if ( rtWidth > 0 && rtHeight > 0 ) { const x = rtWidth * pass . viewport . x ; const y = rtHeight * pass . viewport . y ; const w = rtWidth * pass . viewport . w ; const h = rtHeight * pass . viewport . h ; pass . viewportX = x ; pass . viewportY = y ; pass . viewportW = w ; pass . viewportH = h ; } for ( let i = 0 ; i < pass . resolveTextureInputIDs . length ; i ++ ) { const resolveTextureID = pass . resolveTextureInputIDs [ i ] ; pass . resolveTextureInputTextures [ i ] = this . acquireResolveTextureInputTextureForID ( graph , resolveTextureID ) ; } for ( let i = 0 ; i < pass . renderTargetIDs . length ; i ++ ) this . releaseRenderTargetForID ( pass . renderTargetIDs [ i ] , true ) ; for ( let slot = 0 ; slot < pass . renderTargetExtraRefs . length ; slot ++ ) if ( pass . renderTargetExtraRefs [ slot ] ) this . releaseRenderTargetForID ( pass . renderTargetIDs [ slot ] , true ) ; } private scheduleGraph ( graph ) { assert ( this . renderTargetOutputCount . length === 0 ) ; assert ( this . renderTargetResolveCount . length === 0 ) ; assert ( this . resolveTextureUseCount . length === 0 ) ; for ( let i = 0 ; i < this . renderTargetDeadPool . length ; i ++ ) this . renderTargetDeadPool [ i ] . age ++ ; for ( let i = 0 ; i < this . singleSampledTextureDeadPool . length ; i ++ ) this . singleSampledTextureDeadPool [ i ] . age ++ ; fillArray ( this . renderTargetOutputCount , graph . renderTargetDescriptions . length , 0 ) ; fillArray ( this . renderTargetResolveCount , graph . renderTargetDescriptions . length , 0 ) ; fillArray ( this . resolveTextureUseCount , graph . resolveTextureRenderTargetIDs . length , 0 ) ; for ( let i = 0 ; i < graph . passes . length ; i ++ ) this . scheduleAddUseCount ( graph , graph . passes [ i ] ) ; for ( let i = 0 ; i < graph . passes . length ; i ++ ) this . schedulePass ( graph , graph . passes [ i ] ) ; for ( let i = 0 ; i < this . renderTargetOutputCount . length ; i ++ ) assert ( this . renderTargetOutputCount [ i ] === 0 ) ; for ( let i = 0 ; i < this . renderTargetResolveCount . length ; i ++ ) assert ( this . renderTargetResolveCount [ i ] === 0 ) ; for ( let i = 0 ; i < this . resolveTextureUseCount . length ; i ++ ) assert ( this . resolveTextureUseCount [ i ] === 0 ) ; for ( let i = 0 ; i < this . renderTargetAliveForID . length ; i ++ ) assert ( this . renderTargetAliveForID [ i ] === undefined ) ; const ageThreshold = 1 ; for ( let i = 0 ; i < this . renderTargetDeadPool . length ; i ++ ) { if ( this . renderTargetDeadPool [ i ] . age >= ageThreshold ) { this . renderTargetDeadPool [ i ] . destroy ( this . device ) ; this . renderTargetDeadPool . splice ( i -- , 1 ) ; } } for ( let i = 0 ; i < this . singleSampledTextureDeadPool . length ; i ++ ) { if ( this . singleSampledTextureDeadPool [ i ] . age >= ageThreshold ) { this . singleSampledTextureDeadPool [ i ] . destroy ( this . device ) ; this . singleSampledTextureDeadPool . splice ( i -- , 1 ) ; } } this . renderTargetResolveCount . length = 0 ; this . renderTargetOutputCount . length = 0 ; this . resolveTextureUseCount . length = 0 ; } private execPass ( pass ) { assert ( this . currentPass === null ) ; this . currentPass = pass ; const renderPass = this . device . createRenderPass ( pass . descriptor ) ; renderPass . beginDebugGroup ( pass . debugName ) ; renderPass . setViewport ( pass . viewportX , pass . viewportY , pass . viewportW , pass . viewportH ) ; if ( pass . execFunc !== null ) pass . execFunc ( renderPass , this ) ; renderPass . endDebugGroup ( ) ; this . device . submitPass ( renderPass ) ; if ( pass . postFunc !== null ) pass . postFunc ( this ) ; this . currentPass = null ; } private execGraph ( graph ) { this . scheduleGraph ( graph ) ; for ( let i = 0 ; i < graph . passes . length ; i ++ ) this . execPass ( graph . passes [ i ] ) ; this . singleSampledTextureForResolveTextureID . length = 0 ; } public execute ( builder ) { assert ( builder === this ) ; const graph = assertExists ( this . currentGraph ) ; this . execGraph ( graph ) ; this . currentGraph = null ; } public getDebug ( ) { return this ; } public getPasses ( ) { return this . currentGraph ! . passes ; } public getPassDebugThumbnails ( pass ) { return ( pass as PassImpl ) . debugThumbnails ; } public getPassRenderTargetID ( pass , slot ) { return ( pass as PassImpl ) . renderTargetIDs [ slot ] ; } public getRenderTargetIDDebugName ( renderTargetID ) { return this . currentGraph ! . renderTargetDebugNames [ renderTargetID ] ; } public getPassDebugName ( pass ) { return ( pass as PassImpl ) . debugName ; } public getResolveTextureForID ( resolveTextureID ) { const currentGraphPass = this . currentPass ! ; const i = currentGraphPass . resolveTextureInputIDs . indexOf ( resolveTextureID ) ; assert ( i >= 0 ) ; return assertExists ( currentGraphPass . resolveTextureInputTextures [ i ] ) ; } public getRenderTargetAttachment ( slot ) { const currentGraphPass = this . currentPass ! ; const renderTarget = currentGraphPass . renderTargets [ slot ] ; if ( ! renderTarget ) return null ; return renderTarget . attachment ; } public getRenderTargetTexture ( slot ) { const currentGraphPass = this . currentPass ! ; const renderTarget = currentGraphPass . renderTargets [ slot ] ; if ( ! renderTarget ) return null ; return renderTarget . texture ; } public newGraphBuilder ( ) { this . beginGraphBuilder ( ) ; return this ; } public destroy ( ) { for ( let i = 0 ; i < this . renderTargetAliveForID . length ; i ++ ) assert ( this . renderTargetAliveForID [ i ] === undefined ) ; for ( let i = 0 ; i < this . singleSampledTextureForResolveTextureID . length ; i ++ ) assert ( this . singleSampledTextureForResolveTextureID [ i ] === undefined ) ; for ( let i = 0 ; i < this . renderTargetDeadPool . length ; i ++ ) this . renderTargetDeadPool [ i ] . destroy ( this . device ) ; for ( let i = 0 ; i < this . singleSampledTextureDeadPool . length ; i ++ ) this . singleSampledTextureDeadPool [ i ] . destroy ( this . device ) ; } }