Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSAA render targets: Resolve the depth texture (if any) and allow for manual resolve #15888

Merged
merged 7 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 70 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,56 @@ 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;
}

const bufferBits = rtWrapper._generateDepthBuffer ? gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT : gl.COLOR_BUFFER_BIT;
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
33 changes: 32 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,11 @@ 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;

/**
* 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 +202,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 +568,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
47 changes: 34 additions & 13 deletions packages/dev/core/src/Engines/thinEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1140,21 +1140,12 @@ 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 (texture.isMulti) {
// This texture is part of a MRT texture, we need to treat all attachments
this.unBindMultiColorAttachmentFramebuffer(texture, disableGenerateMipMaps, onBeforeUnbind);
return;
}
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 (!webglRTWrapper.disableAutomaticMSAAResolve) {
this.resolveFramebuffer(texture);
}

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

if (onBeforeUnbind) {
Expand All @@ -1168,6 +1159,36 @@ 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;
}

const bufferBits = rtWrapper._generateDepthBuffer ? gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT : gl.COLOR_BUFFER_BIT;
sebavan marked this conversation as resolved.
Show resolved Hide resolved

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
19 changes: 19 additions & 0 deletions packages/dev/core/src/Engines/webgpuEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3415,6 +3415,25 @@ export class WebGPUEngine extends ThinWebGPUEngine {
this._cacheRenderPipeline.setMRTAttachments(this._mrtAttachments);
}

/**
* 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 texture to resolve
*/
public resolveFramebuffer(_texture: RenderTargetWrapper): void {
throw new Error("resolveFramebuffer is not yet implemented in WebGPU!");
}

/**
* Unbind the current render target and bind the default framebuffer
*/
Expand Down
Loading