export type CallbackFunction = ( ) => void ; export interface IDataToBufferTransfer { buffer : GPUBuffer ; data : ArrayBufferView ; offset : number ; callback : CallbackFunction ; } export interface IDataToTextureTransfer { texture : GPUTexture ; data : ArrayBufferView ; width : number ; height : number ; depth : number ; bytesPerRow : number ; callback : CallbackFunction ; } const SRC_COPY_BUFFER_SIZE = 2 ** 18 ; export class QueueCommander { private _device : GPUDevice = null ; private _srcCopyBuffer : GPUBuffer = null ; private _dataToBufferTransfers : IDataToBufferTransfer [ ] = [ ] ; private _dataToTextureTransfers : IDataToTextureTransfer [ ] = [ ] ; param device public constructor ( device ) { this . _device = device ; this . _srcCopyBuffer = this . _createBuffer ( SRC_COPY_BUFFER_SIZE , GPUBufferUsage . COPY_SRC | GPUBufferUsage . MAP_WRITE ) ; } param size param usage private _createBuffer ( size , usage ) { const buffer = this . _device . createBuffer ( { size : size , usage : usage , mappedAtCreation : false , } ) ; return buffer ; } param buffer param data param byteOffset public transferDataToBuffer ( buffer , data , byteOffset = 0x0 , callback = null ) { this . _dataToBufferTransfers . push ( { buffer , data , offset : byteOffset , callback } ) ; } param buffer param data public transferDataToTexture ( texture , data , width = 0 , height = 0 , depth = 0 , bytesPerRow = 0 , callback = null ) { this . _dataToTextureTransfers . push ( { texture , data , width , height , depth , bytesPerRow , callback } ) ; } /**
     * Flush and execute all queued operations
     */ public flush ( ) { this . _flushDataToBufferTransfers ( ) ; this . _flushDataToTextureTransfers ( ) ; } /**
     * Flush all data to buffer transfers
     */ private _flushDataToBufferTransfers ( ) { const device = this . _device ; const queue = device . defaultQueue ; const dataToBufferTransfers = this . _dataToBufferTransfers ; if ( ! dataToBufferTransfers . length ) return ; for ( let ii = 0 ; ii < dataToBufferTransfers . length ; ++ ii ) { const { buffer , data , offset } = dataToBufferTransfers [ ii ] ; queue . writeBuffer ( buffer , offset , data ) ; dataToBufferTransfers . splice ( ii -- , 1 ) ; } for ( const { callback } of dataToBufferTransfers ) { if ( callback instanceof Function ) callback ( ) ; } return ; const srcCopyBuffer = this . _srcCopyBuffer ; const srcCopyBufferView = new Uint8Array ( srcCopyBuffer . getMappedRange ( 0x0 , SRC_COPY_BUFFER_SIZE ) ) ; const rawQueueBufferCopies = [ ] ; const commandEncoder = device . createCommandEncoder ( { } ) ; { let byteOffset = 0x0 ; for ( let ii = 0 ; ii < dataToBufferTransfers . length ; ++ ii ) { const { buffer , data , offset } = dataToBufferTransfers [ ii ] ; const dataBytes = new Uint8Array ( data . buffer , data . byteOffset , data . byteLength ) ; if ( byteOffset + dataBytes . byteLength > srcCopyBufferView . byteLength ) { rawQueueBufferCopies . push ( dataToBufferTransfers [ ii ] ) ; dataToBufferTransfers . splice ( ii -- , 1 ) ; continue ; } srcCopyBufferView . set ( dataBytes , byteOffset ) ; commandEncoder . copyBufferToBuffer ( srcCopyBuffer , byteOffset , buffer , offset , dataBytes . byteLength ) ; byteOffset += dataBytes . byteLength ; dataToBufferTransfers . splice ( ii -- , 1 ) ; } } srcCopyBuffer . unmap ( ) ; queue . submit ( [ commandEncoder . finish ( ) ] ) ; for ( let ii = 0 ; ii < rawQueueBufferCopies . length ; ++ ii ) { const { buffer , data , offset } = rawQueueBufferCopies [ ii ] ; queue . writeBuffer ( buffer , offset , data ) ; rawQueueBufferCopies . splice ( ii -- , 1 ) ; } for ( const { callback } of dataToBufferTransfers ) { if ( callback instanceof Function ) callback ( ) ; } } /**
     * Flush all data to texture transfers
     */ private _flushDataToTextureTransfers ( ) { const device = this . _device ; const queue = device . defaultQueue ; const dataToTextureTransfers = this . _dataToTextureTransfers ; if ( ! dataToTextureTransfers . length ) return ; for ( let ii = 0 ; ii < dataToTextureTransfers . length ; ++ ii ) { const { texture , data , bytesPerRow , width , height , depth } = dataToTextureTransfers [ ii ] ; queue . writeTexture ( { texture , origin : { x : 0 , y : 0 , z : 0 } } , data , { offset : 0x0 , bytesPerRow , rowsPerImage : 0 } , { width , height , depth } ) ; dataToTextureTransfers . splice ( ii -- , 1 ) ; } for ( const { callback } of dataToTextureTransfers ) { if ( callback instanceof Function ) callback ( ) ; } } /**
     * Destroy this Object
     */ public destroy ( ) { this . _device = null ; this . _srcCopyBuffer = null ; this . _dataToBufferTransfers = null ; this . _dataToTextureTransfers = null ; } }