Skip to content

Commit

Permalink
WebGPURenderer: MSAA, Postprocessing and Wireframe support in the Web…
Browse files Browse the repository at this point in the history
…GL Backend (#27473)

* support material.wireframe in webgl backend

* fix reattach framebuffer, support msaa and wireframe demo

* fix depth samples === 0

* cleanup

* more cleanup

* more cleanup to the webgl pipeline

* postprocessing support

* fix msaa

* fix example afterimage

* improved state management and performances

* generate a new webgl slot for fbos

* revert puppeteer

* fix copyFrameBufferToTexture with correct unbinding and cleanup

* fix multipass pipeline in webgl backend and enable new demos

* regenerate screenshot with gaussian blur
  • Loading branch information
RenaudRohlinger committed Jan 3, 2024
1 parent d28e94e commit aeb40de
Show file tree
Hide file tree
Showing 18 changed files with 693 additions and 107 deletions.
3 changes: 2 additions & 1 deletion examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,8 @@
"webgpu_tsl_editor",
"webgpu_tsl_transpiler",
"webgpu_video_panorama",
"webgpu_postprocessing_afterimage"
"webgpu_postprocessing_afterimage",
"webgpu_multisampled_renderbuffers"
],
"webaudio": [
"webaudio_orientation",
Expand Down
3 changes: 3 additions & 0 deletions examples/jsm/nodes/display/AfterImageNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ class AfterImageNode extends TempNode {
this.damp = uniform( damp );

this._compRT = new RenderTarget();
this._compRT.texture.name = 'AfterImageNode.comp';

this._oldRT = new RenderTarget();
this._oldRT.texture.name = 'AfterImageNode.old';

this.updateBeforeType = NodeUpdateType.RENDER;

Expand Down
2 changes: 2 additions & 0 deletions examples/jsm/nodes/display/GaussianBlurNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ class GaussianBlurNode extends TempNode {
this._passDirection = uniform( new Vector2() );

this._horizontalRT = new RenderTarget();
this._horizontalRT.texture.name = 'GaussianBlurNode.horizontal';
this._verticalRT = new RenderTarget();
this._verticalRT.texture.name = 'GaussianBlurNode.vertical';

this.updateBeforeType = NodeUpdateType.RENDER;

Expand Down
172 changes: 121 additions & 51 deletions examples/jsm/renderers/webgl/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class WebGLBackend extends Backend {

//

//

renderContextData.previousContext = this._currentContext;
this._currentContext = renderContext;

Expand All @@ -79,7 +81,6 @@ class WebGLBackend extends Backend {
this.clear( renderContext.clearColor, renderContext.clearDepth, renderContext.clearStencil, renderContext );

//

if ( renderContext.viewport ) {

this.updateViewport( renderContext );
Expand Down Expand Up @@ -110,11 +111,49 @@ class WebGLBackend extends Backend {

finishRender( renderContext ) {

const { gl } = this;
const renderContextData = this.get( renderContext );
const previousContext = renderContextData.previousContext;

this._currentContext = previousContext;


if ( renderContext.textures !== null && renderContext.renderTarget ) {

const renderTargetContextData = this.get( renderContext.renderTarget );

const { samples, stencilBuffer } = renderContext.renderTarget;
const fb = renderTargetContextData.framebuffer;

if ( samples > 0 ) {

const invalidationArray = [];
const depthStyle = stencilBuffer ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;

invalidationArray.push( gl.COLOR_ATTACHMENT0 );

if ( renderTargetContextData.depthBuffer ) {

invalidationArray.push( depthStyle );

}

// TODO For loop support MRT
const msaaFrameBuffer = renderTargetContextData.msaaFrameBuffer;

gl.bindFramebuffer( gl.READ_FRAMEBUFFER, msaaFrameBuffer );
gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );


gl.blitFramebuffer( 0, 0, renderContext.width, renderContext.height, 0, 0, renderContext.width, renderContext.height, gl.COLOR_BUFFER_BIT, gl.NEAREST );

gl.invalidateFramebuffer( gl.READ_FRAMEBUFFER, invalidationArray );

}


}

if ( previousContext !== null ) {

this._setFramebuffer( previousContext );
Expand All @@ -133,6 +172,9 @@ class WebGLBackend extends Backend {

}


this._currentContext = renderContext;

const occlusionQueryCount = renderContext.occlusionQueryCount;

if ( occlusionQueryCount > 0 ) {
Expand All @@ -151,6 +193,7 @@ class WebGLBackend extends Backend {

}


}

resolveOccludedAsync( renderContext ) {
Expand Down Expand Up @@ -312,7 +355,7 @@ class WebGLBackend extends Backend {

draw( renderObject, info ) {

const { pipeline, material, context } = renderObject;
const { pipeline, material, context, isRenderObject } = renderObject;
const { programGPU, vaoGPU } = this.get( pipeline );

const { gl, state } = this;
Expand All @@ -321,6 +364,13 @@ class WebGLBackend extends Backend {

//

if ( isRenderObject ) {

// we need to bind the framebuffer per object in multi pass pipeline
this._setFramebuffer( context );

}

const bindings = renderObject.getBindings();

for ( const binding of bindings ) {
Expand All @@ -334,8 +384,7 @@ class WebGLBackend extends Backend {

} else if ( binding.isSampledTexture ) {

gl.activeTexture( gl.TEXTURE0 + index );
gl.bindTexture( bindingData.glTextureType, bindingData.textureGPU );
state.bindTexture( bindingData.glTextureType, bindingData.textureGPU, gl.TEXTURE0 + index );

}

Expand Down Expand Up @@ -391,7 +440,20 @@ class WebGLBackend extends Backend {
else if ( object.isLineSegments ) mode = gl.LINES;
else if ( object.isLine ) mode = gl.LINE_STRIP;
else if ( object.isLineLoop ) mode = gl.LINE_LOOP;
else mode = gl.TRIANGLES;
else {

if ( material.wireframe === true ) {

state.setLineWidth( material.wireframeLinewidth * this.renderer.getPixelRatio() );
mode = gl.LINES;

} else {

mode = gl.TRIANGLES;

}

}

//

Expand Down Expand Up @@ -482,6 +544,7 @@ class WebGLBackend extends Backend {

}


destroyTexture( texture ) {

this.textureUtils.destroyTexture( texture );
Expand Down Expand Up @@ -794,100 +857,107 @@ class WebGLBackend extends Backend {

copyFramebufferToTexture( texture, renderContext ) {

const { gl } = this;
this.textureUtils.copyFramebufferToTexture( texture, renderContext );

const { textureGPU } = this.get( texture );
}

const width = texture.image.width;
const height = texture.image.height;
_setFramebuffer( renderContext ) {

gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null );
const { gl, state } = this;

if ( texture.isDepthTexture ) {
let fb = null;
let currentFrameBuffer = null;

const fb = gl.createFramebuffer();
if ( renderContext.textures !== null ) {

gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb );
const renderTargetContextData = this.get( renderContext.renderTarget );
const { samples } = renderContext.renderTarget;

gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureGPU, 0 );
fb = renderTargetContextData.framebuffer;
let msaaFb = renderTargetContextData.msaaFrameBuffer;
let depthRenderbuffer = renderTargetContextData.depthRenderbuffer;

gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, gl.DEPTH_BUFFER_BIT, gl.NEAREST );

gl.deleteFramebuffer( fb );
if ( fb === undefined ) {

fb = gl.createFramebuffer();

} else {
state.bindFramebuffer( gl.FRAMEBUFFER, fb );

gl.bindTexture( gl.TEXTURE_2D, textureGPU );
gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height );
const textures = renderContext.textures;

gl.bindTexture( gl.TEXTURE_2D, null );
for ( let i = 0; i < textures.length; i ++ ) {

}
const texture = textures[ i ];
const textureData = this.get( texture );
textureData.renderTarget = renderContext.renderTarget;

if ( texture.generateMipmaps ) this.generateMipmaps( texture );
const attachment = gl.COLOR_ATTACHMENT0 + i;

this._setFramebuffer( renderContext );

}
gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, textureData.textureGPU, 0 );

_setFramebuffer( renderContext ) {
}

const { gl } = this;
if ( renderContext.depthTexture !== null ) {

if ( renderContext.textures !== null ) {
const textureData = this.get( renderContext.depthTexture );

const renderContextData = this.get( renderContext.renderTarget );
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureData.textureGPU, 0 );

let fb = renderContextData.framebuffer;
}

if ( fb === undefined ) {

fb = gl.createFramebuffer();
renderTargetContextData.framebuffer = fb;

gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
state.drawBuffers( renderContext, fb );

const textures = renderContext.textures;
}

const drawBuffers = [];
if ( samples > 0 ) {

for ( let i = 0; i < textures.length; i ++ ) {
if ( msaaFb === undefined ) {

const texture = textures[ i ];
const { textureGPU } = this.get( texture );
msaaFb = gl.createFramebuffer();

const attachment = gl.COLOR_ATTACHMENT0 + i;
state.bindFramebuffer( gl.FRAMEBUFFER, msaaFb );

gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, textureGPU, 0 );
// TODO For loop support MRT
const msaaRenderbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer( gl.RENDERBUFFER, msaaRenderbuffer );

drawBuffers.push( attachment );
const texture = renderContext.textures[ 0 ];
const textureData = this.get( texture );

}
gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, textureData.glInternalFormat, renderContext.width, renderContext.height );
gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, msaaRenderbuffer );

gl.drawBuffers( drawBuffers );
renderTargetContextData.msaaRenderbuffer = msaaRenderbuffer;
renderTargetContextData.msaaFrameBuffer = msaaFb;

if ( renderContext.depthTexture !== null ) {
if ( depthRenderbuffer === undefined ) {

const { textureGPU } = this.get( renderContext.depthTexture );
depthRenderbuffer = gl.createRenderbuffer();
this.textureUtils.setupRenderBufferStorage( depthRenderbuffer, renderContext );

gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureGPU, 0 );
renderTargetContextData.depthRenderbuffer = depthRenderbuffer;

}

}

renderContextData.framebuffer = fb;
currentFrameBuffer = renderTargetContextData.msaaFrameBuffer;

} else {

gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
currentFrameBuffer = fb;

}

} else {

gl.bindFramebuffer( gl.FRAMEBUFFER, null );

}

state.bindFramebuffer( gl.FRAMEBUFFER, currentFrameBuffer );

}

}
Expand Down
Loading

0 comments on commit aeb40de

Please sign in to comment.