Skip to content

WebGLBackend: Bring back 3D functionality for copyTextureToTexture #30584

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

Merged
merged 2 commits into from
Mar 4, 2025
Merged
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
7 changes: 4 additions & 3 deletions src/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2467,14 +2467,15 @@ class Renderer {
* @param {Texture} dstTexture - The destination texture.
* @param {Box2|Box3} [srcRegion=null] - A bounding box which describes the source region. Can be two or three-dimensional.
* @param {Vector2|Vector3} [dstPosition=null] - A vector that represents the origin of the destination region. Can be two or three-dimensional.
* @param {number} level - The mipmap level to copy.
* @param {number} [srcLevel=0] - The source mip level to copy from.
* @param {number} [dstLevel=0] - The destination mip level to copy to.
*/
copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0 ) {
copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, srcLevel = 0, dstLevel = 0 ) {

this._textures.updateTexture( srcTexture );
this._textures.updateTexture( dstTexture );

this.backend.copyTextureToTexture( srcTexture, dstTexture, srcRegion, dstPosition, level );
this.backend.copyTextureToTexture( srcTexture, dstTexture, srcRegion, dstPosition, srcLevel, dstLevel );

}

Expand Down
7 changes: 4 additions & 3 deletions src/renderers/webgl-fallback/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -1894,11 +1894,12 @@ class WebGLBackend extends Backend {
* @param {Texture} dstTexture - The destination texture.
* @param {?Vector4} [srcRegion=null] - The region of the source texture to copy.
* @param {?(Vector2|Vector3)} [dstPosition=null] - The destination position of the copy.
* @param {number} [level=0] - The mip level to copy.
* @param {number} [srcLevel=0] - The source mip level to copy from.
* @param {number} [dstLevel=0] - The destination mip level to copy to.
*/
copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0 ) {
copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, srcLevel = 0, dstLevel = 0 ) {

this.textureUtils.copyTextureToTexture( srcTexture, dstTexture, srcRegion, dstPosition, level );
this.textureUtils.copyTextureToTexture( srcTexture, dstTexture, srcRegion, dstPosition, srcLevel, dstLevel );

}

Expand Down
86 changes: 67 additions & 19 deletions src/renderers/webgl-fallback/utils/WebGLTextureUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -697,70 +697,94 @@ class WebGLTextureUtils {
*
* @param {Texture} srcTexture - The source texture.
* @param {Texture} dstTexture - The destination texture.
* @param {?Vector4} [srcRegion=null] - The region of the source texture to copy.
* @param {?(Box3|Box2)} [srcRegion=null] - The region of the source texture to copy.
* @param {?(Vector2|Vector3)} [dstPosition=null] - The destination position of the copy.
* @param {number} [level=0] - The mip level to copy.
* @param {number} [srcLevel=0] - The source mip level to copy from.
* @param {number} [dstLevel=0] - The destination mip level to copy to.
*/
copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, level = 0 ) {
copyTextureToTexture( srcTexture, dstTexture, srcRegion = null, dstPosition = null, srcLevel = 0, dstLevel = 0 ) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you mind updating all copyTextureToTexture() signatures in WebGPURenderer? For that, you have to update Renderer.copyTextureToTexture(), Backend.copyTextureToTexture() (the interface definition) and both backends.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool I've updated the signatures too, hopefully it's looking alright


const { gl, backend } = this;
const { state } = this.backend;

const { textureGPU: dstTextureGPU, glTextureType, glType, glFormat } = backend.get( dstTexture );

let width, height, minX, minY;
let dstX, dstY;
state.bindTexture( glTextureType, dstTextureGPU );

// gather the necessary dimensions to copy
let width, height, depth, minX, minY, minZ;
let dstX, dstY, dstZ;
const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ dstLevel ] : srcTexture.image;
Copy link
Collaborator

@Mugen87 Mugen87 Mar 3, 2025

Choose a reason for hiding this comment

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

This bit isn't correct. The default value of dstLevel must be 0 otherwise you end up with srcTexture.mipmaps[ null ].

Please change the default value of dstLevel to 0 in all signatures as well in #30618.

Copy link
Collaborator

@Mugen87 Mugen87 Mar 3, 2025

Choose a reason for hiding this comment

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

Regarding your comment in #30584 (comment): The signature in WebGLRenderer can't be used as a template since the value is null for backwards compatibility reason, AFAICS. Besides, it is set to 0 in the first if statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes that makes sense, I have updated them to use dstLevel = 0 by default now.


if ( srcRegion !== null ) {

width = srcRegion.max.x - srcRegion.min.x;
height = srcRegion.max.y - srcRegion.min.y;
depth = srcRegion.isBox3 ? srcRegion.max.z - srcRegion.min.z : 1;
minX = srcRegion.min.x;
minY = srcRegion.min.y;
minZ = srcRegion.isBox3 ? srcRegion.min.z : 0;

} else {

width = srcTexture.image.width;
height = srcTexture.image.height;
const levelScale = Math.pow( 2, - srcLevel );
width = Math.floor( image.width * levelScale );
height = Math.floor( image.height * levelScale );

if ( srcTexture.isDataArrayTexture ) {

depth = image.depth;

} else if ( srcTexture.isData3DTexture ) {

depth = Math.floor( image.depth * levelScale );

} else {

depth = 1;

}

minX = 0;
minY = 0;
minZ = 0;

}

if ( dstPosition !== null ) {

dstX = dstPosition.x;
dstY = dstPosition.y;
dstZ = dstPosition.z;

} else {

dstX = 0;
dstY = 0;
dstZ = 0;

}

state.bindTexture( glTextureType, dstTextureGPU );

// As another texture upload may have changed pixelStorei
// parameters, make sure they are correct for the dstTexture
gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );

// used for copying data from cpu
const currentUnpackRowLen = gl.getParameter( gl.UNPACK_ROW_LENGTH );
const currentUnpackImageHeight = gl.getParameter( gl.UNPACK_IMAGE_HEIGHT );
const currentUnpackSkipPixels = gl.getParameter( gl.UNPACK_SKIP_PIXELS );
const currentUnpackSkipRows = gl.getParameter( gl.UNPACK_SKIP_ROWS );
const currentUnpackSkipImages = gl.getParameter( gl.UNPACK_SKIP_IMAGES );

const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ level ] : srcTexture.image;

gl.pixelStorei( gl.UNPACK_ROW_LENGTH, image.width );
gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, image.height );
gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, minX );
gl.pixelStorei( gl.UNPACK_SKIP_ROWS, minY );
gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, minZ );

// set up the src texture
const isDst3D = dstTexture.isDataArrayTexture || dstTexture.isData3DTexture;
if ( srcTexture.isRenderTargetTexture || srcTexture.isDepthTexture ) {

const srcTextureData = backend.get( srcTexture );
Expand All @@ -786,39 +810,63 @@ class WebGLTextureUtils {

} else {

if ( srcTexture.isDataTexture ) {
if ( isDst3D ) {

gl.texSubImage2D( gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image.data );
// copy data into the 3d texture
if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) {

gl.texSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image.data );

} else if ( dstTexture.isCompressedArrayTexture ) {

gl.compressedTexSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, image.data );

} else {

gl.texSubImage3D( glTextureType, dstLevel, dstX, dstY, dstZ, width, height, depth, glFormat, glType, image );

}

} else {

if ( srcTexture.isCompressedTexture ) {
// copy data into the 2d texture
if ( srcTexture.isDataTexture ) {

gl.texSubImage2D( glTextureType, dstLevel, dstX, dstY, width, height, glFormat, glType, image.data );

gl.compressedTexSubImage2D( gl.TEXTURE_2D, level, dstX, dstY, image.width, image.height, glFormat, image.data );
} else if ( srcTexture.isCompressedTexture ) {

gl.compressedTexSubImage2D( glTextureType, dstLevel, dstX, dstY, image.width, image.height, glFormat, image.data );

} else {

gl.texSubImage2D( gl.TEXTURE_2D, level, dstX, dstY, width, height, glFormat, glType, image );
gl.texSubImage2D( glTextureType, dstLevel, dstX, dstY, width, height, glFormat, glType, image );

}

}

}

// reset values
gl.pixelStorei( gl.UNPACK_ROW_LENGTH, currentUnpackRowLen );
gl.pixelStorei( gl.UNPACK_IMAGE_HEIGHT, currentUnpackImageHeight );
gl.pixelStorei( gl.UNPACK_SKIP_PIXELS, currentUnpackSkipPixels );
gl.pixelStorei( gl.UNPACK_SKIP_ROWS, currentUnpackSkipRows );
gl.pixelStorei( gl.UNPACK_SKIP_IMAGES, currentUnpackSkipImages );

// Generate mipmaps only when copying level 0
if ( level === 0 && dstTexture.generateMipmaps ) gl.generateMipmap( gl.TEXTURE_2D );
if ( dstLevel === 0 && dstTexture.generateMipmaps ) {

gl.generateMipmap( glTextureType );

}

state.unbindTexture();

}


/**
* Copies the current bound framebuffer to the given texture.
*
Expand Down