import { vec4 } from 'gl-matrix' ; import { assert , logIf , LogLevel } from './auxiliaries' ; import { v4 } from './gl-matrix-extensions' ; import { Context } from './context' ; import { Framebuffer } from './framebuffer' ; import { Initializable } from './initializable' ; import { NdcFillingTriangle } from './ndcfillingtriangle' ; import { Program } from './program' ; import { Shader } from './shader' ; import { Texture2D } from './texture2d' ; /**
 * This rendering pass blits the color attachment of a given rgba-framebuffer into the target buffer provided on frame.
 * For rendering, a direct blit is used. However, if this is not supported, a textured, screen-aligned triangle is used
 * for blitting as fallback.
 *
 * The blit pass can be used as follows:
 * ```
 * this._blitPass = new BlitPass(this._context);
 * this._blitPass.initialize();
 *
 * this._blitPass.readBuffer = gl2facade.COLOR_ATTACHMENT0;
 * // this._blitPass.srcBounds = vec4.fromValues(0, 0, this._sourceSize[0], this._sourceSize[1]);
 * this._blitPass.filter = gl.LINEAR;
 * this._blitPass.target = this._defaultFBO;
 * this._blitPass.drawBuffer = gl.BACK;
 *
 * this.blitPass.framebuffer = this.intermediateFBO;
 * // this.blitPass.dstBounds = vec4.fromValues(dstX0, dstY0, dstX1, dstY1);
 * this.blitPass.frame();
 * ```
 */ export class BlitPass extends Initializable { /**
     * Read-only access to the objects context, used to get context information and WebGL API access.
     */ protected _context : Context ; see target protected _target : Framebuffer ; see framebuffer protected _framebuffer : Framebuffer ; see readBuffer protected _readBuffer : GLenum ; see drawBuffer protected _drawBuffer : GLenum ; see filter protected _filter : GLenum ; see srcBounds protected _srcBounds : vec4 | undefined ; see dstBounds protected _dstBounds : vec4 | undefined ; see forceProgramBlit protected _enforceProgramBlit = false ; /**
     * Geometry used to draw on. This is not provided by default to allow for geometry sharing. If no triangle is given,
     * the ndc triangle will be created and managed internally.
     */ protected _ndcTriangle : NdcFillingTriangle ; /**
     * Tracks ownership of the ndc-filling triangle.
     */ protected _ndcTriangleShared = false ; protected _program : Program ; /**
     * Uniform for passing the filter to blit: true for nearest, false otherwise (linear).
     */ protected _uNearest : WebGLUniformLocation ; /**
     * Uniform for passing the source bounds to blit.
     */ protected _uSrcBounds : WebGLUniformLocation ; /**
     * Uniform for passing the destination bounds to blit.
     */ protected _uDstBounds : WebGLUniformLocation ; constructor ( context ) { super ( ) ; this . _context = context ; } /**
     * Uses direct blit via glBlitFramebuffer for blitting a single read buffer into the given target's draw buffer.
     */ private functionBlit ( ) { const gl = this . _context . gl ; this . _target . bind ( gl . DRAW_FRAMEBUFFER ) ; this . _framebuffer . bind ( gl . READ_FRAMEBUFFER ) ; gl . readBuffer ( this . _readBuffer ) ; if ( this . _drawBuffer >= gl . COLOR_ATTACHMENT1 && this . _drawBuffer <= gl . COLOR_ATTACHMENT15 ) { const offset = this . _drawBuffer - gl . COLOR_ATTACHMENT0 ; const drawBuffers = new Array ( offset + 1 ) . fill ( gl . NONE ) ; drawBuffers [ offset ] = this . _drawBuffer ; gl . drawBuffers ( drawBuffers ) ; } else { gl . drawBuffers ( [ this . _drawBuffer ] ) ; } /**
         * The glClear is somehow required to make the blit work. Reducing the clear area to zero is intended to reduce
         * the actual load of clear.
         */ gl . enable ( gl . SCISSOR_TEST ) ; gl . scissor ( 0 , 0 , 0 , 0 ) ; gl . clear ( gl . COLOR_BUFFER_BIT ) ; gl . disable ( gl . SCISSOR_TEST ) ; const srcBounds = this . _srcBounds ? this . _srcBounds : [ 0 , 0 , this . _framebuffer . width , this . _framebuffer . height ] ; const dstBounds = this . _dstBounds ? this . _dstBounds : [ 0 , 0 , this . _target . width , this . _target . height ] ; gl . blitFramebuffer ( srcBounds [ 0 ] , srcBounds [ 1 ] , srcBounds [ 2 ] , srcBounds [ 3 ] , dstBounds [ 0 ] , dstBounds [ 1 ] , dstBounds [ 2 ] , dstBounds [ 3 ] , gl . COLOR_BUFFER_BIT , this . _filter ) ; this . _framebuffer . unbind ( gl . READ_FRAMEBUFFER ) ; this . _target . unbind ( gl . DRAW_FRAMEBUFFER ) ; } param program private programBlit ( ) { if ( this . _program === undefined ) { this . createProgram ( ) ; } assert ( this . _ndcTriangle && this . _ndcTriangle . initialized , `expected an initialized ndc triangle` ) ; const gl = this . _context . gl ; const srcBounds = this . _srcBounds ? this . _srcBounds : vec4 . fromValues ( 0 , 0 , this . _framebuffer . width , this . _framebuffer . height ) ; const dstBounds = this . _dstBounds ? this . _dstBounds : vec4 . fromValues ( 0 , 0 , this . _target . width , this . _target . height ) ; const srcBoundsNormalized = vec4 . div ( v4 ( ) , srcBounds , [ this . _framebuffer . width , this . _framebuffer . height , this . _framebuffer . width , this . _framebuffer . height ] ) ; const dstBoundsNormalized = vec4 . div ( v4 ( ) , dstBounds , [ this . _target . width , this . _target . height , this . _target . width , this . _target . height ] ) ; gl . viewport ( dstBounds [ 0 ] , dstBounds [ 1 ] , dstBounds [ 2 ] - dstBounds [ 0 ] , dstBounds [ 3 ] - dstBounds [ 1 ] ) ; gl . disable ( gl . DEPTH_TEST ) ; gl . depthMask ( false ) ; this . _program . bind ( ) ; gl . uniform4fv ( this . _uSrcBounds , srcBoundsNormalized ) ; gl . uniform4fv ( this . _uDstBounds , dstBoundsNormalized ) ; gl . uniform1i ( this . _uNearest , this . filter === gl . nearest ) ; const texture = this . _framebuffer . texture ( this . _readBuffer ) as Texture2D ; texture . bind ( gl . TEXTURE0 ) ; const target = this . _context . isWebGL2 ? gl . DRAW_FRAMEBUFFER : gl . FRAMEBUFFER ; this . _target . bind ( target ) ; this . _ndcTriangle . bind ( ) ; this . _ndcTriangle . draw ( ) ; this . _ndcTriangle . unbind ( ) ; this . _target . unbind ( target ) ; texture . unbind ( ) ; gl . enable ( gl . DEPTH_TEST ) ; gl . depthMask ( true ) ; } /**
     * Used to create (on-demand) the blit program for program based blitting. This function can be specialized, e.g.,
     * for creating custom blit passes such as the `DebugPass`  DebugPass . This method assumes the program to be
     * undefined.
      protected createProgram ( ) { assert ( this . _program === undefined , `expected blit program to be undefined before its creation` ) ; const gl = this . _context . gl ; const vert = new Shader ( this . _context , gl . VERTEX_SHADER , 'blit.vert (blit)' ) ; vert . initialize ( require ( './shaders/blit.vert' ) ) ; const frag = new Shader ( this . _context , gl . FRAGMENT_SHADER , 'blit.frag (blit)' ) ; frag . initialize ( require ( './shaders/blit.frag' ) ) ; this . _program = new Program ( this . _context , 'BlitProgram' ) ; this . _program . initialize ( [ vert , frag ] , false ) ; if ( ! this . _ndcTriangle . initialized ) { this . _ndcTriangle . initialize ( ) ; } this . _program . attribute ( 'a_vertex' , this . _ndcTriangle . vertexLocation ) ; this . _program . link ( ) ; this . _uSrcBounds = this . _program . uniform ( 'u_srcBounds' ) ; this . _uDstBounds = this . _program . uniform ( 'u_dstBounds' ) ; this . _uNearest = this . _program . uniform ( 'u_nearest' ) ; this . _program . bind ( ) ; gl . uniform1i ( this . _program . uniform ( 'u_source' ) , 0 ) ; this . _program . unbind ( ) ; return this . _program . valid ; } param ndcTriangle @ Initializable . initialize ( ) initialize ( ndcTriangle ? ) { const gl = this . _context . gl ; if ( ndcTriangle === undefined ) { this . _ndcTriangle = new NdcFillingTriangle ( this . _context , 'NdcFillingTriangle-Blit' ) ; } else { this . _ndcTriangle = ndcTriangle ; this . _ndcTriangleShared = true ; } this . _filter = gl . NEAREST ; return true ; } /**
     * Specializes this pass's uninitialization. Program and geometry resources are released (if allocated). Cached
     * uniform and attribute locations are invalidated.
     */ @ Initializable . uninitialize ( ) uninitialize ( ) { if ( ! this . _ndcTriangleShared && this . _ndcTriangle . initialized ) { this . _ndcTriangle . uninitialize ( ) ; } this . _program . uninitialize ( ) ; } /**
     * A blit frame either blits or draws the color attachment of the given framebuffer to the target framebuffer. For
     * program based/indirect blit a viewport filling area, i.e., a screen-aligned triangle is used.
     */ @ Initializable . assert_initialized ( ) frame ( ) { logIf ( ! this . _target || ! this . _target . valid , LogLevel . Warning , `valid target expected, given ${ this . _target }` ) ; logIf ( ! this . _framebuffer || ! this . _framebuffer . valid , LogLevel . Warning , `valid framebuffer for blitting from expected, given ${ this . _framebuffer }` ) ; const gl = this . _context . gl ; switch ( this . _readBuffer ) { case gl . DEPTH_ATTACHMENT : case gl . STENCIL_ATTACHMENT : case gl . DEPTH_STENCIL_ATTACHMENT : return this . programBlit ( ) ; default : break ; } if ( this . _context . supportsBlitFramebuffer && this . _enforceProgramBlit === false ) { return this . functionBlit ( ) ; } this . programBlit ( ) ; } param framebuffer set framebuffer ( framebuffer ) { this . assertInitialized ( ) ; this . _framebuffer = framebuffer ; } param readBuffer set readBuffer ( readBuffer ) { this . assertInitialized ( ) ; this . _readBuffer = readBuffer ; } param drawBuffer set drawBuffer ( drawBuffer ) { this . assertInitialized ( ) ; this . _drawBuffer = drawBuffer ; } param target set target ( target ) { this . assertInitialized ( ) ; this . _target = target ; } /**
     * Specifies the interpolation to be applied if the image is stretched. Must be GL_NEAREST or GL_LINEAR.
     */ set filter ( filter ) { this . _filter = filter ; } param bounds set srcBounds ( bounds ) { this . _srcBounds = bounds ? vec4 . clone ( bounds ) : undefined ; } param bounds set dstBounds ( bounds ) { this . _dstBounds = bounds ? vec4 . clone ( bounds ) : undefined ; } param enforce set enforceProgramBlit ( enforce ) { this . _enforceProgramBlit = enforce ; } }