Skip to content

Commit

Permalink
MSAA render targets: Resolve the depth texture (if any) and allow for…
Browse files Browse the repository at this point in the history
… manual resolve (#15888)

* Resolve depth texture (if any) and allow for manual resolve

* Split resolve and generate mipmaps

* Decrease size of ThinEngine

* Fix visu tests

* Resolve stencil too

* Add switches to selectively enable MSAA resolves

* Add doc
  • Loading branch information
Popov72 authored Nov 26, 2024
1 parent bcedd77 commit 8ab1527
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 57 deletions.
108 changes: 73 additions & 35 deletions packages/dev/core/src/Engines/Extensions/engine.multiRender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ declare module "../../Engines/abstractEngine" {
*/
updateMultipleRenderTargetTextureSampleCount(rtWrapper: Nullable<RenderTargetWrapper>, samples: number, initializeBuffers?: boolean): number;

/**
* Generates mipmaps for the texture of the (multi) render target
* @param texture The render target containing the textures to generate the mipmaps for
*/
generateMipMapsMultiFramebuffer(texture: RenderTargetWrapper): void;

/**
* Resolves the MSAA textures of the (multi) render target into their non-MSAA version.
* Note that if "texture" is not a MSAA render target, no resolve is performed.
* @param texture The render target texture containing the MSAA textures to resolve
*/
resolveMultiFramebuffer(texture: RenderTargetWrapper): void;

/**
* Select a subsets of attachments to draw to.
* @param attachments gl attachments
Expand Down Expand Up @@ -108,43 +121,12 @@ ThinEngine.prototype.unBindMultiColorAttachmentFramebuffer = function (
): void {
this._currentRenderTarget = null;

// If MSAA, we need to bitblt back to main texture
const gl = this._gl;

const attachments = rtWrapper._attachments!;
const count = attachments.length;

if (rtWrapper._MSAAFramebuffer) {
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, rtWrapper._MSAAFramebuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, rtWrapper._framebuffer);

for (let i = 0; i < count; i++) {
const texture = rtWrapper.textures![i];

for (let j = 0; j < count; j++) {
attachments[j] = gl.NONE;
}

attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
gl.readBuffer(attachments[i]);
gl.drawBuffers(attachments);
gl.blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, texture.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
}

for (let i = 0; i < count; i++) {
attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
}

gl.drawBuffers(attachments);
if (!rtWrapper.disableAutomaticMSAAResolve) {
this.resolveMultiFramebuffer(rtWrapper);
}

for (let i = 0; i < count; i++) {
const texture = rtWrapper.textures![i];
if (texture?.generateMipMaps && !disableGenerateMipMaps && !texture?.isCube && !texture?.is3D) {
this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
gl.generateMipmap(gl.TEXTURE_2D);
this._bindTextureDirectly(gl.TEXTURE_2D, null);
}
if (!disableGenerateMipMaps) {
this.generateMipMapsMultiFramebuffer(rtWrapper);
}

if (onBeforeUnbind) {
Expand Down Expand Up @@ -508,3 +490,59 @@ ThinEngine.prototype.updateMultipleRenderTargetTextureSampleCount = function (

return samples;
};

ThinEngine.prototype.generateMipMapsMultiFramebuffer = function (texture: RenderTargetWrapper): void {
const rtWrapper = texture as WebGLRenderTargetWrapper;
const gl = this._gl;

if (!rtWrapper.isMulti) {
return;
}

for (let i = 0; i < rtWrapper._attachments!.length; i++) {
const texture = rtWrapper.textures![i];
if (texture?.generateMipMaps && !texture?.isCube && !texture?.is3D) {
this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
gl.generateMipmap(gl.TEXTURE_2D);
this._bindTextureDirectly(gl.TEXTURE_2D, null);
}
}
};

ThinEngine.prototype.resolveMultiFramebuffer = function (texture: RenderTargetWrapper): void {
const rtWrapper = texture as WebGLRenderTargetWrapper;
const gl = this._gl;

if (!rtWrapper._MSAAFramebuffer || !rtWrapper.isMulti) {
return;
}

let bufferBits = rtWrapper.resolveMSAAColors ? gl.COLOR_BUFFER_BIT : 0;
bufferBits |= rtWrapper._generateDepthBuffer && rtWrapper.resolveMSAADepth ? gl.DEPTH_BUFFER_BIT : 0;
bufferBits |= rtWrapper._generateStencilBuffer && rtWrapper.resolveMSAAStencil ? gl.STENCIL_BUFFER_BIT : 0;

const attachments = rtWrapper._attachments!;
const count = attachments.length;

gl.bindFramebuffer(gl.READ_FRAMEBUFFER, rtWrapper._MSAAFramebuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, rtWrapper._framebuffer);

for (let i = 0; i < count; i++) {
const texture = rtWrapper.textures![i];

for (let j = 0; j < count; j++) {
attachments[j] = gl.NONE;
}

attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
gl.readBuffer(attachments[i]);
gl.drawBuffers(attachments);
gl.blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, texture.height, bufferBits, gl.NEAREST);
}

for (let i = 0; i < count; i++) {
attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
}

gl.drawBuffers(attachments);
};
11 changes: 11 additions & 0 deletions packages/dev/core/src/Engines/WebGL/webGLRenderTargetWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,17 @@ export class WebGLRenderTargetWrapper extends RenderTargetWrapper {
}
}

public override resolveMSAATextures(): void {
const engine = this._engine as ThinEngine;
const currentFramebuffer = engine._currentFramebuffer;

engine._bindUnboundFramebuffer(this._MSAAFramebuffer);

super.resolveMSAATextures();

engine._bindUnboundFramebuffer(currentFramebuffer);
}

public override dispose(disposeOnlyFramebuffers = this._disposeOnlyFramebuffers): void {
const gl = this._context;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ declare module "../../abstractEngine" {
*/
updateMultipleRenderTargetTextureSampleCount(rtWrapper: Nullable<RenderTargetWrapper>, samples: number, initializeBuffers?: boolean): number;

/**
* Generates mipmaps for the texture of the (multi) render target
* @param texture The render target containing the textures to generate the mipmaps for
*/
generateMipMapsMultiFramebuffer(texture: RenderTargetWrapper): void;

/**
* Resolves the MSAA textures of the (multi) render target into their non-MSAA version.
* Note that if "texture" is not a MSAA render target, no resolve is performed.
* @param texture The render target texture containing the MSAA textures to resolve
*/
resolveMultiFramebuffer(texture: RenderTargetWrapper): void;

/**
* Select a subsets of attachments to draw to.
* @param attachments gl attachments
Expand Down Expand Up @@ -75,16 +88,10 @@ WebGPUEngine.prototype.unBindMultiColorAttachmentFramebuffer = function (
onBeforeUnbind();
}

const attachments = rtWrapper._attachments!;
const count = attachments.length;

this._endCurrentRenderPass();

for (let i = 0; i < count; i++) {
const texture = rtWrapper.textures![i];
if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube && !texture.is3D) {
this._generateMipmaps(texture);
}
if (!disableGenerateMipMaps) {
this.generateMipMapsMultiFramebuffer(rtWrapper);
}

this._currentRenderTarget = null;
Expand Down Expand Up @@ -306,6 +313,28 @@ WebGPUEngine.prototype.updateMultipleRenderTargetTextureSampleCount = function (
return samples;
};

WebGPUEngine.prototype.generateMipMapsMultiFramebuffer = function (texture: RenderTargetWrapper): void {
const rtWrapper = texture as WebGPURenderTargetWrapper;

if (!rtWrapper.isMulti) {
return;
}

const attachments = rtWrapper._attachments!;
const count = attachments.length;

for (let i = 0; i < count; i++) {
const texture = rtWrapper.textures![i];
if (texture.generateMipMaps && !texture.isCube && !texture.is3D) {
this._generateMipmaps(texture);
}
}
};

WebGPUEngine.prototype.resolveMultiFramebuffer = function (_texture: RenderTargetWrapper): void {
throw new Error("resolveMultiFramebuffer is not yet implemented in WebGPU!");
};

WebGPUEngine.prototype.bindAttachments = function (attachments: number[]): void {
if (attachments.length === 0 || !this._currentRenderTarget) {
return;
Expand Down
13 changes: 13 additions & 0 deletions packages/dev/core/src/Engines/abstractEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,19 @@ export abstract class AbstractEngine {
*/
public abstract unBindFramebuffer(texture: RenderTargetWrapper, disableGenerateMipMaps?: boolean, onBeforeUnbind?: () => void): void;

/**
* Generates mipmaps for the texture of the (single) render target
* @param texture The render target containing the texture to generate the mipmaps for
*/
public abstract generateMipMapsFramebuffer(texture: RenderTargetWrapper): void;

/**
* Resolves the MSAA texture of the (single) render target into its non-MSAA version.
* Note that if "texture" is not a MSAA render target, no resolve is performed.
* @param texture The render target texture containing the MSAA texture to resolve
*/
public abstract resolveFramebuffer(texture: RenderTargetWrapper): void;

/**Gets driver info if available */
public abstract extractDriverInfo(): string;

Expand Down
49 changes: 48 additions & 1 deletion packages/dev/core/src/Engines/renderTargetWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,27 @@ export class RenderTargetWrapper {
return this._layerIndices;
}

/**
* Sets this property to true to disable the automatic MSAA resolve that happens when the render target wrapper is unbound (default is false)
*/
public disableAutomaticMSAAResolve = false;

/**
* Indicates if MSAA color texture(s) should be resolved when a resolve occur (either automatically by the engine or manually by the user) (default is true)
* Note that you can trigger a MSAA resolve at any time by calling resolveMSAATextures()
*/
public resolveMSAAColors = true;

/**
* Indicates if MSAA depth texture should be resolved when a resolve occur (either automatically by the engine or manually by the user) (default is false)
*/
public resolveMSAADepth = false;

/**
* Indicates if MSAA stencil texture should be resolved when a resolve occur (either automatically by the engine or manually by the user) (default is false)
*/
public resolveMSAAStencil = false;

/**
* Gets the base array layer of a texture in the textures array
* This is an number that is calculated based on the layer and face indices set for this texture at that index
Expand Down Expand Up @@ -197,6 +218,32 @@ export class RenderTargetWrapper {
return result;
}

/**
* Resolves the MSAA textures into their non-MSAA version.
* Note that if samples equals 1 (no MSAA), no resolve is performed.
*/
public resolveMSAATextures(): void {
if (this.isMulti) {
this._engine.resolveMultiFramebuffer(this);
} else {
this._engine.resolveFramebuffer(this);
}
}

/**
* Generates mipmaps for each texture of the render target
*/
public generateMipMaps(): void {
if (this._engine._currentRenderTarget === this) {
this._engine.unBindFramebuffer(this, true);
}
if (this.isMulti) {
this._engine.generateMipMapsMultiFramebuffer(this);
} else {
this._engine.generateMipMapsFramebuffer(this);
}
}

/**
* Initializes the render target wrapper
* @param isMulti true if the wrapper is a multi render target
Expand Down Expand Up @@ -537,7 +584,7 @@ export class RenderTargetWrapper {
*/
public releaseTextures(): void {
if (this._textures) {
for (let i = 0; i < this._textures?.length ?? 0; ++i) {
for (let i = 0; i < this._textures.length; ++i) {
this._textures[i].dispose();
}
}
Expand Down
53 changes: 42 additions & 11 deletions packages/dev/core/src/Engines/thinEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1140,21 +1140,20 @@ export class ThinEngine extends AbstractEngine {

this._currentRenderTarget = null;

// If MSAA, we need to bitblt back to main texture
const gl = this._gl;
if (webglRTWrapper._MSAAFramebuffer) {
if (!webglRTWrapper.disableAutomaticMSAAResolve) {
if (texture.isMulti) {
// This texture is part of a MRT texture, we need to treat all attachments
this.unBindMultiColorAttachmentFramebuffer(texture, disableGenerateMipMaps, onBeforeUnbind);
return;
this.resolveMultiFramebuffer(texture);
} else {
this.resolveFramebuffer(texture);
}
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, webglRTWrapper._MSAAFramebuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, webglRTWrapper._framebuffer);
gl.blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, texture.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
}

if (texture.texture?.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
this.generateMipmaps(texture.texture);
if (!disableGenerateMipMaps) {
if (texture.isMulti) {
this.generateMipMapsMultiFramebuffer(texture);
} else {
this.generateMipMapsFramebuffer(texture);
}
}

if (onBeforeUnbind) {
Expand All @@ -1168,6 +1167,38 @@ export class ThinEngine extends AbstractEngine {
this._bindUnboundFramebuffer(null);
}

/**
* Generates mipmaps for the texture of the (single) render target
* @param texture The render target containing the texture to generate the mipmaps for
*/
public generateMipMapsFramebuffer(texture: RenderTargetWrapper): void {
if (!texture.isMulti && texture.texture?.generateMipMaps && !texture.isCube) {
this.generateMipmaps(texture.texture);
}
}

/**
* Resolves the MSAA texture of the (single) render target into its non-MSAA version.
* Note that if "texture" is not a MSAA render target, no resolve is performed.
* @param texture The render target texture containing the MSAA textures to resolve
*/
public resolveFramebuffer(texture: RenderTargetWrapper): void {
const rtWrapper = texture as WebGLRenderTargetWrapper;
const gl = this._gl;

if (!rtWrapper._MSAAFramebuffer || rtWrapper.isMulti) {
return;
}

let bufferBits = rtWrapper.resolveMSAAColors ? gl.COLOR_BUFFER_BIT : 0;
bufferBits |= rtWrapper._generateDepthBuffer && rtWrapper.resolveMSAADepth ? gl.DEPTH_BUFFER_BIT : 0;
bufferBits |= rtWrapper._generateStencilBuffer && rtWrapper.resolveMSAAStencil ? gl.STENCIL_BUFFER_BIT : 0;

gl.bindFramebuffer(gl.READ_FRAMEBUFFER, rtWrapper._MSAAFramebuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, rtWrapper._framebuffer);
gl.blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, texture.height, bufferBits, gl.NEAREST);
}

/**
* Force a webGL flush (ie. a flush of all waiting webGL commands)
*/
Expand Down
Loading

0 comments on commit 8ab1527

Please sign in to comment.