import { AbstractRenderer , IRendererOptions } from "./AbstractRenderer" ; import { Frame } from "../Frame" ; import { AbstractCamera } from "../Camera" ; import { IBindGroupResource , RenderPipelineGenerator , ToWGPUTextureFormat } from "../Material/RenderPipelineGenerator" ; import { Buffer } from "../Buffer" ; import { Sampler } from "../Sampler" ; import { Texture } from "../Texture" ; import { QueueCommander } from "../QueueCommander" ; import { LoadGLSLang , GetTimeStamp } from "../utils" ; import { TEXTURE_FORMAT } from "../constants" ; export interface IUniformBindingEntry { id : number ; data : any ; } export class Renderer extends AbstractRenderer { private _device : GPUDevice = null ; private _adapter : GPUAdapter = null ; private _context : GPUCanvasContext = null ; private _swapchain : GPUSwapChain = null ; private _beginFrameTimestamp : number = 0 ; private _lastFrameTimestamp : number = 0 ; private _queueCommander : QueueCommander = null ; private _swapchainTexture : Texture = null ; private _swapchainFormat : TEXTURE_FORMAT = null ; param options public constructor ( options ? ) { super ( options ) ; } /**
     * The WGPU device
     */ public getDevice ( ) { return this . _device ; } /**
     * The WGPU adapter
     */ public getAdapter ( ) { return this . _adapter ; } /**
     * The WGPU canvas context
     */ public getContext ( ) { return this . _context ; } /**
     * The WGPU swapchain
     */ public getSwapchain ( ) { return this . _swapchain ; } /**
     * The queue commander
     */ public getQueueCommander ( ) { return this . _queueCommander ; } /**
     * The swapchain texture
     */ public getSwapchainTexture ( ) { return this . _swapchainTexture ; } /**
     * The texture format of the swapchain
     */ public getSwapchainFormat ( ) { return this . _swapchainFormat ; } /**
     * Create the renderer
     */ public async create ( ) { await LoadGLSLang ( ) ; this . _adapter = await this . _createAdapter ( ) ; this . _device = await this . _createDevice ( ) ; this . _context = this . _createContext ( ) ; this . _swapchainFormat = await this . _resolveSwapchainFormat ( ) ; this . _swapchain = this . _createSwapchain ( ) ; this . _swapchainTexture = new Texture ( { width : this . getWidth ( ) , height : this . getHeight ( ) , format : this . getSwapchainFormat ( ) } ) ; this . _queueCommander = new QueueCommander ( this . getDevice ( ) ) ; this . _beginFrameTimestamp = GetTimeStamp ( ) ; this . _lastFrameTimestamp = GetTimeStamp ( ) ; this . resize ( this . getCanvas ( ) . width , this . getCanvas ( ) . height ) ; this . _updateSwapchainTexture ( ) ; return this ; } param width param height public resize ( width , height ) { width = Math . max ( 1 , width ) ; height = Math . max ( 1 , height ) ; super . resize ( width , height ) ; } param frame public render ( frame ) { const now = GetTimeStamp ( ) ; const delta = ( now - this . _lastFrameTimestamp ) / 1e3 ; const begin = this . _beginFrameTimestamp ; const time = ( now - begin ) / 1e3 ; this . emit ( "beforerender" , { time , delta } ) ; if ( ! ( frame instanceof Frame ) ) throw new TypeError ( `Unexpected type for argument 1 in 'render', expected instance of 'Frame'` ) ; const camera = frame . getAttachedCamera ( ) ; if ( camera instanceof AbstractCamera ) { if ( ( this . getWidth ( ) !== camera . getWidth ( ) ) || ( this . getHeight ( ) !== camera . getHeight ( ) ) ) this . resize ( camera . getWidth ( ) , camera . getHeight ( ) ) ; } if ( ! this . getAdapter ( ) || ! this . getDevice ( ) ) throw new ReferenceError ( `Method 'create' must be called on 'Renderer' before usage` ) ; frame . draw ( this ) ; this . _lastFrameTimestamp = now ; this . emit ( "afterrender" , { time , delta } ) ; } /**
     * Flushes all pending operations such as buffer copies
     */ public flush ( ) { this . getQueueCommander ( ) . flush ( ) ; } param queue param resources public processUniformBindingQueue ( queue , uniformResources ) { for ( let ii = 0 ; ii < queue . length ; ++ ii ) { const { id , data } = queue [ ii ] ; const uniformResource = uniformResources . find ( resource => resource ? resource . id === id : false ) ; if ( uniformResource === null ) { throw new ReferenceError ( `Failed to resolve uniform resource for uniform '${ id }'` ) ; } if ( data instanceof Buffer ) { if ( ! data . getResource ( ) ) { const descriptor = RenderPipelineGenerator . GenerateBufferDescriptor ( data ) ; data . create ( this , descriptor ) ; } uniformResource . resource = data . getResource ( ) ; } else if ( data instanceof Sampler ) { if ( ! data . getResource ( ) ) { const descriptor = RenderPipelineGenerator . GenerateSamplerDescriptor ( data ) ; data . create ( this , descriptor ) ; } uniformResource . resource = data . getResource ( ) ; } else if ( data instanceof Texture ) { if ( ! data . getResource ( ) ) { const descriptor = RenderPipelineGenerator . GenerateTextureDescriptor ( data ) ; data . create ( this , descriptor ) ; } uniformResource . resource = data . getResourceView ( ) ; } else { throw new TypeError ( `Unexpected type '${ typeof data }'` ) ; } queue . splice ( ii -- , 1 ) ; } } param resources public processUniformBindings ( bindings ) { const entries = bindings . entries ( ) ; for ( const [ _key , binding ] of entries ) { if ( binding instanceof Buffer ) { binding . update ( this ) ; } else if ( binding instanceof Sampler ) { binding . update ( this ) ; } else if ( binding instanceof Texture ) { binding . update ( this ) ; } } } /**
     * Destroy this Object
     */ public destroy ( ) { this . _device = null ; this . _adapter = null ; this . _context = null ; this . _swapchain = null ; super . destroy ( ) ; } /**
     * Resolves the texture format of the swapchain
     */ private async _resolveSwapchainFormat ( ) { const device = this . getDevice ( ) ; const context = this . getContext ( ) ; const swapchainFormat = await context . getSwapChainPreferredFormat ( device ) ; const start = TEXTURE_FORMAT . NONE + 1 ; const end = Object . keys ( TEXTURE_FORMAT ) . length ; for ( let ii = start ; ii < end ; ++ ii ) { const format = ToWGPUTextureFormat ( ii ) ; if ( format === swapchainFormat ) return ii ; } return TEXTURE_FORMAT . NONE ; } /**
     * Creates a WGPU adapter
     */ private async _createAdapter ( ) { if ( typeof GPU === "undefined" || navigator . gpu === void 0 || ! ( navigator . gpu instanceof GPU ) ) throw new ReferenceError ( `WebGPU is not available` ) ; const adapter = await navigator . gpu . requestAdapter ( ) ; if ( ! ( adapter instanceof GPUAdapter ) ) throw new ReferenceError ( `Failed to request GPU adapter` ) ; return adapter ; } /**
     * Creates a WGPU device
     */ private async _createDevice ( ) { if ( ! this . getAdapter ( ) . extensions . includes ( "texture-compression-bc" ) ) throw new ReferenceError ( `Required extension 'texture-compression-bc' is unavailable` ) ; const device = await this . getAdapter ( ) . requestDevice ( { extensions : [ "texture-compression-bc" ] } ) ; if ( ! ( device instanceof GPUDevice ) ) throw new ReferenceError ( `Failed to create GPU device` ) ; device . addEventListener ( "uncapturederror" , ( error ) => { this . _onDeviceError ( error ) ; } ) ; return device ; } /**
     * Creates a WGPU context
     */ private _createContext ( ) { const context = this . getCanvas ( ) . getContext ( "gpupresent" ) ; if ( ! ( context instanceof GPUCanvasContext ) ) throw new ReferenceError ( `Failed to retrieve GPU context` ) ; return context ; } /**
     * Creates a WGPU swapchain
     */ private _createSwapchain ( ) { const swapchain = this . getContext ( ) . configureSwapChain ( { device : this . getDevice ( ) , format : ToWGPUTextureFormat ( this . getSwapchainFormat ( ) ) , usage : GPUTextureUsage . OUTPUT_ATTACHMENT } ) ; if ( ! ( swapchain instanceof GPUSwapChain ) ) throw new ReferenceError ( `Failed to configure GPU swapchain` ) ; return swapchain ; } param error private _onDeviceError ( error ) { this . emit ( "deviceerror" , error ) ; } /**
     * Update the swapchain texture
     */ private _updateSwapchainTexture ( ) { const canvas = this . getCanvas ( ) ; const swapchain = this . getSwapchain ( ) ; const swapchainTexture = swapchain . getCurrentTexture ( ) ; const swapchainTextureView = swapchainTexture . createView ( ) ; ( this . _swapchainTexture as any ) . _resource = swapchainTexture ; ( this . _swapchainTexture as any ) . _resourceView = swapchainTextureView ; ( this . _swapchainTexture as any ) . _width = canvas . clientWidth ; ( this . _swapchainTexture as any ) . _height = canvas . clientHeight ; } }