let _vector2 = null;
let _color4 = null;
import Color4 from './Color4.js';
import { Vector2 } from '../../math/Vector2.js';
import { createCanvasElement, warnOnce } from '../../utils.js';
import { REVISION } from '../../constants.js';
/**
* Most of the rendering related logic is implemented in the
* {@link module:Renderer} module and related management components.
* Sometimes it is required though to execute commands which are
* specific to the current 3D backend (which is WebGPU or WebGL 2).
* This abstract base class defines an interface that encapsulates
* all backend-related logic. Derived classes for each backend must
* implement the interface.
*
* @abstract
* @private
*/
class Backend {
/**
* Constructs a new backend.
*
* @param {Object} parameters - An object holding parameters for the backend.
*/
constructor( parameters = {} ) {
/**
* The parameters of the backend.
*
* @type {Object}
*/
this.parameters = Object.assign( {}, parameters );
/**
* This weak map holds backend-specific data of objects
* like textures, attributes or render targets.
*
* @type {WeakMap}
*/
this.data = new WeakMap();
/**
* A reference to the renderer.
*
* @type {Renderer?}
* @default null
*/
this.renderer = null;
/**
* A reference to the canvas element the renderer is drawing to.
*
* @type {(HTMLCanvasElement|OffscreenCanvas)?}
* @default null
*/
this.domElement = null;
/**
* A reference to the timestamp query pool.
*
* @type {{render: TimestampQueryPool?, compute: TimestampQueryPool?}}
*/
this.timestampQueryPool = {
'render': null,
'compute': null
};
}
/**
* Initializes the backend so it is ready for usage. Concrete backends
* are supposed to implement their rendering context creation and related
* operations in this method.
*
* @async
* @param {Renderer} renderer - The renderer.
* @return {Promise} A Promise that resolves when the backend has been initialized.
*/
async init( renderer ) {
this.renderer = renderer;
}
/**
* The coordinate system of the backend.
*
* @abstract
* @type {Number}
* @readonly
*/
get coordinateSystem() {}
// render context
/**
* This method is executed at the beginning of a render call and
* can be used by the backend to prepare the state for upcoming
* draw calls.
*
* @abstract
* @param {RenderContext} renderContext - The render context.
*/
beginRender( /*renderContext*/ ) {}
/**
* This method is executed at the end of a render call and
* can be used by the backend to finalize work after draw
* calls.
*
* @abstract
* @param {RenderContext} renderContext - The render context.
*/
finishRender( /*renderContext*/ ) {}
/**
* This method is executed at the beginning of a compute call and
* can be used by the backend to prepare the state for upcoming
* compute tasks.
*
* @abstract
* @param {Node|Array<Node>} computeGroup - The compute node(s).
*/
beginCompute( /*computeGroup*/ ) {}
/**
* This method is executed at the end of a compute call and
* can be used by the backend to finalize work after compute
* tasks.
*
* @abstract
* @param {Node|Array<Node>} computeGroup - The compute node(s).
*/
finishCompute( /*computeGroup*/ ) {}
// render object
/**
* Executes a draw command for the given render object.
*
* @abstract
* @param {RenderObject} renderObject - The render object to draw.
* @param {Info} info - Holds a series of statistical information about the GPU memory and the rendering process.
*/
draw( /*renderObject, info*/ ) { }
// compute node
/**
* Executes a compute command for the given compute node.
*
* @abstract
* @param {Node|Array<Node>} computeGroup - The group of compute nodes of a compute call. Can be a single compute node.
* @param {Node} computeNode - The compute node.
* @param {Array<BindGroup>} bindings - The bindings.
* @param {ComputePipeline} computePipeline - The compute pipeline.
*/
compute( /*computeGroup, computeNode, computeBindings, computePipeline*/ ) { }
// program
/**
* Creates a shader program from the given programmable stage.
*
* @abstract
* @param {ProgrammableStage} program - The programmable stage.
*/
createProgram( /*program*/ ) { }
/**
* Destroys the shader program of the given programmable stage.
*
* @abstract
* @param {ProgrammableStage} program - The programmable stage.
*/
destroyProgram( /*program*/ ) { }
// bindings
/**
* Creates bindings from the given bind group definition.
*
* @abstract
* @param {BindGroup} bindGroup - The bind group.
* @param {Array<BindGroup>} bindings - Array of bind groups.
* @param {Number} cacheIndex - The cache index.
* @param {Number} version - The version.
*/
createBindings( /*bindGroup, bindings, cacheIndex, version*/ ) { }
/**
* Updates the given bind group definition.
*
* @abstract
* @param {BindGroup} bindGroup - The bind group.
* @param {Array<BindGroup>} bindings - Array of bind groups.
* @param {Number} cacheIndex - The cache index.
* @param {Number} version - The version.
*/
updateBindings( /*bindGroup, bindings, cacheIndex, version*/ ) { }
/**
* Updates a buffer binding.
*
* @abstract
* @param {Buffer} binding - The buffer binding to update.
*/
updateBinding( /*binding*/ ) { }
// pipeline
/**
* Creates a render pipeline for the given render object.
*
* @abstract
* @param {RenderObject} renderObject - The render object.
* @param {Array<Promise>} promises - An array of compilation promises which are used in `compileAsync()`.
*/
createRenderPipeline( /*renderObject, promises*/ ) { }
/**
* Creates a compute pipeline for the given compute node.
*
* @abstract
* @param {ComputePipeline} computePipeline - The compute pipeline.
* @param {Array<BindGroup>} bindings - The bindings.
*/
createComputePipeline( /*computePipeline, bindings*/ ) { }
// cache key
/**
* Returns `true` if the render pipeline requires an update.
*
* @abstract
* @param {RenderObject} renderObject - The render object.
* @return {Boolean} Whether the render pipeline requires an update or not.
*/
needsRenderUpdate( /*renderObject*/ ) { }
/**
* Returns a cache key that is used to identify render pipelines.
*
* @abstract
* @param {RenderObject} renderObject - The render object.
* @return {String} The cache key.
*/
getRenderCacheKey( /*renderObject*/ ) { }
// node builder
/**
* Returns a node builder for the given render object.
*
* @abstract
* @param {RenderObject} renderObject - The render object.
* @param {Renderer} renderer - The renderer.
* @return {NodeBuilder} The node builder.
*/
createNodeBuilder( /*renderObject, renderer*/ ) { }
// textures
/**
* Creates a GPU sampler for the given texture.
*
* @abstract
* @param {Texture} texture - The texture to create the sampler for.
*/
createSampler( /*texture*/ ) { }
/**
* Destroys the GPU sampler for the given texture.
*
* @abstract
* @param {Texture} texture - The texture to destroy the sampler for.
*/
destroySampler( /*texture*/ ) {}
/**
* Creates a default texture for the given texture that can be used
* as a placeholder until the actual texture is ready for usage.
*
* @abstract
* @param {Texture} texture - The texture to create a default texture for.
*/
createDefaultTexture( /*texture*/ ) { }
/**
* Defines a texture on the GPU for the given texture object.
*
* @abstract
* @param {Texture} texture - The texture.
* @param {Object} [options={}] - Optional configuration parameter.
*/
createTexture( /*texture, options={}*/ ) { }
/**
* Uploads the updated texture data to the GPU.
*
* @abstract
* @param {Texture} texture - The texture.
* @param {Object} [options={}] - Optional configuration parameter.
*/
updateTexture( /*texture, options = {}*/ ) { }
/**
* Generates mipmaps for the given texture.
*
* @abstract
* @param {Texture} texture - The texture.
*/
generateMipmaps( /*texture*/ ) { }
/**
* Destroys the GPU data for the given texture object.
*
* @abstract
* @param {Texture} texture - The texture.
*/
destroyTexture( /*texture*/ ) { }
/**
* Returns texture data as a typed array.
*
* @abstract
* @async
* @param {Texture} texture - The texture to copy.
* @param {Number} x - The x coordinate of the copy origin.
* @param {Number} y - The y coordinate of the copy origin.
* @param {Number} width - The width of the copy.
* @param {Number} height - The height of the copy.
* @param {Number} faceIndex - The face index.
* @return {Promise<TypedArray>} A Promise that resolves with a typed array when the copy operation has finished.
*/
async copyTextureToBuffer( /*texture, x, y, width, height, faceIndex*/ ) {}
/**
* Copies data of the given source texture to the given destination texture.
*
* @abstract
* @param {Texture} srcTexture - The source texture.
* @param {Texture} dstTexture - The destination texture.
* @param {Vector4?} [srcRegion=null] - The region of the source texture to copy.
* @param {(Vector2|Vector3)?} [dstPosition=null] - The destination position of the copy.
* @param {Number} [level=0] - The mip level to copy.
*/
copyTextureToTexture( /*srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0*/ ) {}
/**
* Copies the current bound framebuffer to the given texture.
*
* @abstract
* @param {Texture} texture - The destination texture.
* @param {RenderContext} renderContext - The render context.
* @param {Vector4} rectangle - A four dimensional vector defining the origin and dimension of the copy.
*/
copyFramebufferToTexture( /*texture, renderContext, rectangle*/ ) {}
// attributes
/**
* Creates the GPU buffer of a shader attribute.
*
* @abstract
* @param {BufferAttribute} attribute - The buffer attribute.
*/
createAttribute( /*attribute*/ ) { }
/**
* Creates the GPU buffer of an indexed shader attribute.
*
* @abstract
* @param {BufferAttribute} attribute - The indexed buffer attribute.
*/
createIndexAttribute( /*attribute*/ ) { }
/**
* Creates the GPU buffer of a storage attribute.
*
* @abstract
* @param {BufferAttribute} attribute - The buffer attribute.
*/
createStorageAttribute( /*attribute*/ ) { }
/**
* Updates the GPU buffer of a shader attribute.
*
* @abstract
* @param {BufferAttribute} attribute - The buffer attribute to update.
*/
updateAttribute( /*attribute*/ ) { }
/**
* Destroys the GPU buffer of a shader attribute.
*
* @abstract
* @param {BufferAttribute} attribute - The buffer attribute to destroy.
*/
destroyAttribute( /*attribute*/ ) { }
// canvas
/**
* Returns the backend's rendering context.
*
* @abstract
* @return {Object} The rendering context.
*/
getContext() { }
/**
* Backends can use this method if they have to run
* logic when the renderer gets resized.
*
* @abstract
*/
updateSize() { }
/**
* Updates the viewport with the values from the given render context.
*
* @abstract
* @param {RenderContext} renderContext - The render context.
*/
updateViewport( /*renderContext*/ ) {}
// utils
/**
* Returns `true` if the given 3D object is fully occluded by other
* 3D objects in the scene. Backends must implement this method by using
* a Occlusion Query API.
*
* @abstract
* @param {RenderContext} renderContext - The render context.
* @param {Object3D} object - The 3D object to test.
* @return {Boolean} Whether the 3D object is fully occluded or not.
*/
isOccluded( /*renderContext, object*/ ) {}
/**
* Resolves the time stamp for the given render context and type.
*
* @async
* @abstract
* @param {String} [type='render'] - The type of the time stamp.
* @return {Promise<Number>} A Promise that resolves with the time stamp.
*/
async resolveTimestampsAsync( type = 'render' ) {
if ( ! this.trackTimestamp ) {
warnOnce( 'WebGPURenderer: Timestamp tracking is disabled.' );
return;
}
const queryPool = this.timestampQueryPool[ type ];
if ( ! queryPool ) {
warnOnce( `WebGPURenderer: No timestamp query pool for type '${type}' found.` );
return;
}
const duration = await queryPool.resolveQueriesAsync();
this.renderer.info[ type ].timestamp = duration;
return duration;
}
/**
* Can be used to synchronize CPU operations with GPU tasks. So when this method is called,
* the CPU waits for the GPU to complete its operation (e.g. a compute task).
*
* @async
* @abstract
* @return {Promise} A Promise that resolves when synchronization has been finished.
*/
async waitForGPU() {}
/**
* This method performs a readback operation by moving buffer data from
* a storage buffer attribute from the GPU to the CPU.
*
* @async
* @param {StorageBufferAttribute} attribute - The storage buffer attribute.
* @return {Promise<ArrayBuffer>} A promise that resolves with the buffer data when the data are ready.
*/
async getArrayBufferAsync( /* attribute */ ) {}
/**
* Checks if the given feature is supported by the backend.
*
* @async
* @abstract
* @param {String} name - The feature's name.
* @return {Promise<Boolean>} A Promise that resolves with a bool that indicates whether the feature is supported or not.
*/
async hasFeatureAsync( /*name*/ ) { }
/**
* Checks if the given feature is supported by the backend.
*
* @abstract
* @param {String} name - The feature's name.
* @return {Boolean} Whether the feature is supported or not.
*/
hasFeature( /*name*/ ) {}
/**
* Returns the maximum anisotropy texture filtering value.
*
* @abstract
* @return {Number} The maximum anisotropy texture filtering value.
*/
getMaxAnisotropy() {}
/**
* Returns the drawing buffer size.
*
* @return {Vector2} The drawing buffer size.
*/
getDrawingBufferSize() {
_vector2 = _vector2 || new Vector2();
return this.renderer.getDrawingBufferSize( _vector2 );
}
/**
* Defines the scissor test.
*
* @abstract
* @param {Boolean} boolean - Whether the scissor test should be enabled or not.
*/
setScissorTest( /*boolean*/ ) { }
/**
* Returns the clear color and alpha into a single
* color object.
*
* @return {Color4} The clear color.
*/
getClearColor() {
const renderer = this.renderer;
_color4 = _color4 || new Color4();
renderer.getClearColor( _color4 );
_color4.getRGB( _color4, this.renderer.currentColorSpace );
return _color4;
}
/**
* Returns the DOM element. If no DOM element exists, the backend
* creates a new one.
*
* @return {HTMLCanvasElement} The DOM element.
*/
getDomElement() {
let domElement = this.domElement;
if ( domElement === null ) {
domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : createCanvasElement();
// OffscreenCanvas does not have setAttribute, see #22811
if ( 'setAttribute' in domElement ) domElement.setAttribute( 'data-engine', `three.js r${REVISION} webgpu` );
this.domElement = domElement;
}
return domElement;
}
/**
* Sets a dictionary for the given object into the
* internal data structure.
*
* @param {Object} object - The object.
* @param {Object} value - The dictionary to set.
*/
set( object, value ) {
this.data.set( object, value );
}
/**
* Returns the dictionary for the given object.
*
* @param {Object} object - The object.
* @return {Object} The object's dictionary.
*/
get( object ) {
let map = this.data.get( object );
if ( map === undefined ) {
map = {};
this.data.set( object, map );
}
return map;
}
/**
* Checks if the given object has a dictionary
* with data defined.
*
* @param {Object} object - The object.
* @return {Boolean} Whether a dictionary for the given object as been defined or not.
*/
has( object ) {
return this.data.has( object );
}
/**
* Deletes an object from the internal data structure.
*
* @param {Object} object - The object to delete.
*/
delete( object ) {
this.data.delete( object );
}
/**
* Frees internal resources.
*
* @abstract
*/
dispose() { }
}
export default Backend;