Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
321 changes: 194 additions & 127 deletions packages/rhi-webgl/src/GLRenderTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,203 @@
import { GLTexture } from "./GLTexture";
import { WebGLGraphicDevice } from "./WebGLGraphicDevice";

/**
* MSAA manager for WebGL render targets.
* Handles all MSAA-related operations separately from the main render target logic.
*/
class MSAAManager {
private _gl: WebGLRenderingContext & WebGL2RenderingContext;
private _isWebGL2: boolean;
private _target: RenderTarget;
private _frameBuffer: WebGLFramebuffer;
private _colorRenderBuffers: WebGLRenderbuffer[] = [];
private _depthRenderBuffer: WebGLRenderbuffer | null = null;
private _blitDrawBuffers: GLenum[] = [];
private _oriDrawBuffers: GLenum[];

constructor(
gl: WebGLRenderingContext & WebGL2RenderingContext,
isWebGL2: boolean,
target: RenderTarget,
oriDrawBuffers: GLenum[]
) {
this._gl = gl;
this._isWebGL2 = isWebGL2;
this._target = target;
this._oriDrawBuffers = oriDrawBuffers;
this._frameBuffer = this._gl.createFramebuffer();

Check failure on line 39 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`

Check failure on line 39 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix formatting issue

-    this._frameBuffer = this._gl.createFramebuffer();
-    
+    this._frameBuffer = this._gl.createFramebuffer();
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this._frameBuffer = this._gl.createFramebuffer();
🧰 Tools
🪛 GitHub Check: lint

[failure] 39-39:
Delete ····

🪛 ESLint

[error] 39-39: Delete ····

(prettier/prettier)

🪛 GitHub Actions: CI

[error] 39-39: Prettier formatting error: Delete extra spaces (prettier/prettier).

🤖 Prompt for AI Agents
In packages/rhi-webgl/src/GLRenderTarget.ts at line 39, there is a formatting
issue that needs correction. Review the line for any inconsistent indentation,
spacing, or alignment problems and adjust it to match the project's coding style
guidelines for clean and consistent formatting.

this._bindFBO();
}

/**
* Activate MSAA frame buffer for rendering.
*/
activate(): void {
this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, this._frameBuffer);
}

/**
* Resolve MSAA frame buffer to target frame buffer.
*/
resolveTo(targetFrameBuffer: WebGLFramebuffer): void {
const gl = this._gl;
const mask = gl.COLOR_BUFFER_BIT | (this._target.depthTexture ? gl.DEPTH_BUFFER_BIT : 0);
const { colorTextureCount, width, height } = this._target;

gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._frameBuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, targetFrameBuffer);

for (let textureIndex = 0; textureIndex < colorTextureCount; textureIndex++) {
const attachment = gl.COLOR_ATTACHMENT0 + textureIndex;

this._blitDrawBuffers[textureIndex] = attachment;

gl.readBuffer(attachment);
gl.drawBuffers(this._blitDrawBuffers);
gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, gl.NEAREST);

this._blitDrawBuffers[textureIndex] = gl.NONE;
}

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}

/**
* Destroy MSAA resources.
*/
destroy(): void {
const gl = this._gl;

if (this._frameBuffer) {
gl.deleteFramebuffer(this._frameBuffer);
this._frameBuffer = null;
}

if (this._depthRenderBuffer) {
gl.deleteRenderbuffer(this._depthRenderBuffer);
this._depthRenderBuffer = null;
}

for (let i = 0; i < this._colorRenderBuffers.length; i++) {
gl.deleteRenderbuffer(this._colorRenderBuffers[i]);
}
this._colorRenderBuffers.length = 0;
}


Check failure on line 98 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `⏎⏎`

Check failure on line 98 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `⏎⏎`

private _bindFBO(): void {
const gl = this._gl;
const isWebGL2 = this._isWebGL2;

/** @ts-ignore */
const { _depth, colorTextureCount } = this._target;

Check failure on line 105 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `·`

Check failure on line 105 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `·`

this._blitDrawBuffers = new Array(colorTextureCount);

gl.bindFramebuffer(gl.FRAMEBUFFER, this._frameBuffer);

// prepare MRT+MSAA color RBOs
for (let i = 0; i < colorTextureCount; i++) {
this._blitDrawBuffers[i] = gl.NONE;

const internalFormat = /** @ts-ignore */

Check failure on line 115 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎·······`

Check failure on line 115 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎·······`
(this._target.getColorTexture(i)._platformTexture as GLTexture)._formatDetail.internalFormat;

Check failure on line 117 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `······`

Check failure on line 117 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `······`
this._colorRenderBuffers[i] = GLRenderTarget._createRenderBuffer(this._gl, this._target, internalFormat, gl.COLOR_ATTACHMENT0 + i);

Check failure on line 118 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `this._gl,·this._target,·internalFormat,·gl.COLOR_ATTACHMENT0·+·i` with `⏎········this._gl,⏎········this._target,⏎········internalFormat,⏎········gl.COLOR_ATTACHMENT0·+·i⏎······`

Check failure on line 118 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `this._gl,·this._target,·internalFormat,·gl.COLOR_ATTACHMENT0·+·i` with `⏎········this._gl,⏎········this._target,⏎········internalFormat,⏎········gl.COLOR_ATTACHMENT0·+·i⏎······`
}
gl.drawBuffers(this._oriDrawBuffers);

// prepare MSAA depth RBO
if (_depth !== null) {
const { internalFormat, attachment } =
_depth instanceof Texture
? /** @ts-ignore */
(_depth._platformTexture as GLTexture)._formatDetail
: GLTexture._getRenderBufferDepthFormatDetail(_depth, gl, isWebGL2);

this._depthRenderBuffer = GLRenderTarget._createRenderBuffer(this._gl, this._target, internalFormat, attachment);
}

GLRenderTarget._checkFrameBufferStatus(gl);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
}
}

/**
* The render target in WebGL platform is used for off-screen rendering.
*/
export class GLRenderTarget implements IPlatformRenderTarget {
/**
* @internal
*/
static _createRenderBuffer(
gl: WebGLRenderingContext & WebGL2RenderingContext,
target: RenderTarget,
internalFormat: GLenum,
attachment: GLenum
): WebGLRenderbuffer {
const renderBuffer = gl.createRenderbuffer();
const { width, height, antiAliasing } = target;

Check failure on line 154 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`

Check failure on line 154 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`
gl.bindRenderbuffer(gl.RENDERBUFFER, renderBuffer);

Check failure on line 156 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`

Check failure on line 156 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`
if (antiAliasing > 1) {
// Use MSAA storage
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, antiAliasing, internalFormat, width, height);
} else {
// Use regular storage
gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height);
}

Check failure on line 164 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`

Check failure on line 164 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderBuffer);

Check failure on line 166 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`

Check failure on line 166 in packages/rhi-webgl/src/GLRenderTarget.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `····`
return renderBuffer;
}

/**
* @internal
*/
static _checkFrameBufferStatus(gl: WebGLRenderingContext | WebGL2RenderingContext): void {
const e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);

switch (e) {
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
throw new Error(
"The attachment types are mismatched or not all framebuffer attachment points are framebuffer attachment complete"
);
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
throw new Error("There is no attachment");
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
throw new Error(" Height and width of the attachment are not the same.");
case gl.FRAMEBUFFER_UNSUPPORTED:
// #5.14.3 Event Types in https://registry.khronos.org/webgl/specs/1.0.0/
if (!gl.isContextLost()) {
throw new Error(
"The format of the attachment is not supported or if depth and stencil attachments are not the same renderbuffer"
);
}
break;
case (gl as WebGL2RenderingContext).FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: // Only for WebGL2
throw new Error(
"The values of gl.RENDERBUFFER_SAMPLES are different among attached renderbuffers, or are non-zero if the attached images are a mix of renderbuffers and textures."
);
}
}

private _gl: WebGLRenderingContext & WebGL2RenderingContext;
private _isWebGL2: boolean;
private _target: RenderTarget;
private _frameBuffer: WebGLFramebuffer;
private _MSAAFrameBuffer: WebGLFramebuffer | null;
private _depthRenderBuffer: WebGLRenderbuffer | null;
private _MSAAColorRenderBuffers: WebGLRenderbuffer[] = [];
private _MSAADepthRenderBuffer: WebGLRenderbuffer | null;
private _oriDrawBuffers: GLenum[];
private _blitDrawBuffers: GLenum[] | null;
private _curMipLevel: number = 0;
private _curFaceIndex: TextureCubeFace = undefined;

// MSAA manager handles all MSAA-related operations
private _msaaManager: MSAAManager | null = null;
Comment on lines +208 to +210
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix formatting issue

  private _curFaceIndex: TextureCubeFace = undefined;
-  
+
  // MSAA manager handles all MSAA-related operations
  private _msaaManager: MSAAManager | null = null;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// MSAA manager handles all MSAA-related operations
private _msaaManager: MSAAManager | null = null;
private _curFaceIndex: TextureCubeFace = undefined;
// MSAA manager handles all MSAA-related operations
private _msaaManager: MSAAManager | null = null;
🧰 Tools
🪛 GitHub Check: lint

[failure] 193-193:
Delete ··

🪛 ESLint

[error] 193-193: Delete ··

(prettier/prettier)

🤖 Prompt for AI Agents
In packages/rhi-webgl/src/GLRenderTarget.ts around lines 193 to 195, fix the
formatting issue by ensuring consistent indentation and spacing for the comment
and the private variable declaration. Align the comment properly above the
variable declaration without extra blank lines or misaligned spaces.


/**
* Create render target in WebGL platform.
Expand All @@ -40,11 +221,6 @@
const { _colorTextures, _depth, width, height } = target;
const isDepthTexture = _depth instanceof Texture;

/** todo
* MRT + Cube + [,MSAA]
* MRT + MSAA
*/

for (let i = 0, n = _colorTextures.length; i < n; i++) {
const { format, isSRGBColorSpace } = _colorTextures[i];
if (!GLTexture._supportRenderBufferColorFormat(format, rhi)) {
Expand Down Expand Up @@ -91,8 +267,7 @@

// bind MSAA FBO
if (target.antiAliasing > 1) {
this._MSAAFrameBuffer = this._gl.createFramebuffer();
this._bindMSAAFBO();
this._msaaManager = new MSAAManager(this._gl, this._isWebGL2, target, this._oriDrawBuffers);
}
}

Expand Down Expand Up @@ -151,37 +326,18 @@
this._curMipLevel = mipLevel;
this._curFaceIndex = faceIndex;

if (this._MSAAFrameBuffer) {
gl.bindFramebuffer(gl.FRAMEBUFFER, this._MSAAFrameBuffer);
if (this._msaaManager) {
this._msaaManager.activate();
}
}

/**
* Blit FBO.
*/
blitRenderTarget(): void {
if (!this._MSAAFrameBuffer) return;

const gl = this._gl;
const mask = gl.COLOR_BUFFER_BIT | (this._target.depthTexture ? gl.DEPTH_BUFFER_BIT : 0);
const { colorTextureCount, width, height } = this._target;

gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._MSAAFrameBuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._frameBuffer);

for (let textureIndex = 0; textureIndex < colorTextureCount; textureIndex++) {
const attachment = gl.COLOR_ATTACHMENT0 + textureIndex;

this._blitDrawBuffers[textureIndex] = attachment;

gl.readBuffer(attachment);
gl.drawBuffers(this._blitDrawBuffers);
gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, mask, gl.NEAREST);

this._blitDrawBuffers[textureIndex] = gl.NONE;
if (this._msaaManager) {
this._msaaManager.resolveTo(this._frameBuffer);
}

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}

/**
Expand All @@ -192,18 +348,14 @@

this._frameBuffer && gl.deleteFramebuffer(this._frameBuffer);
this._depthRenderBuffer && gl.deleteRenderbuffer(this._depthRenderBuffer);
this._MSAAFrameBuffer && gl.deleteFramebuffer(this._MSAAFrameBuffer);
this._MSAADepthRenderBuffer && gl.deleteRenderbuffer(this._MSAADepthRenderBuffer);

for (let i = 0; i < this._MSAAColorRenderBuffers.length; i++) {
gl.deleteRenderbuffer(this._MSAAColorRenderBuffers[i]);
if (this._msaaManager) {
this._msaaManager.destroy();
this._msaaManager = null;
}

this._frameBuffer = null;
this._depthRenderBuffer = null;
this._MSAAFrameBuffer = null;
this._MSAAColorRenderBuffers.length = 0;
this._MSAADepthRenderBuffer = null;
}

private _bindMainFBO(): void {
Expand Down Expand Up @@ -254,96 +406,11 @@
);
} else if (this._target.antiAliasing <= 1) {
const { internalFormat, attachment } = GLTexture._getRenderBufferDepthFormatDetail(_depth, gl, isWebGL2);
const depthRenderBuffer = gl.createRenderbuffer();

this._depthRenderBuffer = depthRenderBuffer;

gl.bindRenderbuffer(gl.RENDERBUFFER, depthRenderBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, depthRenderBuffer);
this._depthRenderBuffer = GLRenderTarget._createRenderBuffer(gl, this._target, internalFormat, attachment);
}
}

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
}

private _bindMSAAFBO(): void {
const gl = this._gl;
const isWebGL2 = this._isWebGL2;
const MSAADepthRenderBuffer = gl.createRenderbuffer();

/** @ts-ignore */
const { _depth, colorTextureCount, antiAliasing, width, height } = this._target;

this._blitDrawBuffers = new Array(colorTextureCount);
this._MSAADepthRenderBuffer = MSAADepthRenderBuffer;

gl.bindFramebuffer(gl.FRAMEBUFFER, this._MSAAFrameBuffer);

// prepare MRT+MSAA color RBOs
for (let i = 0; i < colorTextureCount; i++) {
const MSAAColorRenderBuffer = gl.createRenderbuffer();

this._MSAAColorRenderBuffers[i] = MSAAColorRenderBuffer;
this._blitDrawBuffers[i] = gl.NONE;

gl.bindRenderbuffer(gl.RENDERBUFFER, MSAAColorRenderBuffer);
gl.renderbufferStorageMultisample(
gl.RENDERBUFFER,
antiAliasing,
/** @ts-ignore */
(this._target.getColorTexture(i)._platformTexture as GLTexture)._formatDetail.internalFormat,
width,
height
);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.RENDERBUFFER, MSAAColorRenderBuffer);
}
gl.drawBuffers(this._oriDrawBuffers);

// prepare MSAA depth RBO
if (_depth !== null) {
const { internalFormat, attachment } =
_depth instanceof Texture
? /** @ts-ignore */
(_depth._platformTexture as GLTexture)._formatDetail
: GLTexture._getRenderBufferDepthFormatDetail(_depth, gl, isWebGL2);

gl.bindRenderbuffer(gl.RENDERBUFFER, MSAADepthRenderBuffer);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, antiAliasing, internalFormat, width, height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, MSAADepthRenderBuffer);
}

this._checkFrameBuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
}

private _checkFrameBuffer(): void {
const gl = this._gl;
const e = gl.checkFramebufferStatus(gl.FRAMEBUFFER);

switch (e) {
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
throw new Error(
"The attachment types are mismatched or not all framebuffer attachment points are framebuffer attachment complete"
);
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
throw new Error("There is no attachment");
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
throw new Error(" Height and width of the attachment are not the same.");
case gl.FRAMEBUFFER_UNSUPPORTED:
// #5.14.3 Event Types in https://registry.khronos.org/webgl/specs/1.0.0/
if (!gl.isContextLost()) {
throw new Error(
"The format of the attachment is not supported or if depth and stencil attachments are not the same renderbuffer"
);
}
break;
case gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: // Only for WebGL2
throw new Error(
"The values of gl.RENDERBUFFER_SAMPLES are different among attached renderbuffers, or are non-zero if the attached images are a mix of renderbuffers and textures."
);
}
}
}
Loading
Loading