Skip to content

Commit 59708a4

Browse files
committed
Texture array updates WIP
1 parent 6d7cdc8 commit 59708a4

File tree

5 files changed

+96
-79
lines changed

5 files changed

+96
-79
lines changed

src/framework/handlers/texture.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,18 @@ const _completePartialMipmapChain = function (texture) {
9898
for (let level = texture._levels.length; level < requiredMipLevels; ++level) {
9999
const width = Math.max(1, texture._width >> (level - 1));
100100
const height = Math.max(1, texture._height >> (level - 1));
101-
if (texture.cubemap) {
101+
if (texture.cubemap || texture.array) {
102102
const mips = [];
103-
for (let face = 0; face < 6; ++face) {
104-
mips.push(downsample(width, height, texture._levels[level - 1][face]));
103+
for (let slice = 0; slice < texture.slices; ++slice) {
104+
mips.push(downsample(width, height, texture._levels[level - 1][slice]));
105105
}
106106
texture._levels.push(mips);
107107
} else {
108108
texture._levels.push(downsample(width, height, texture._levels[level - 1]));
109109
}
110110
}
111111

112-
texture._levelsUpdated = texture.cubemap ? [[true, true, true, true, true, true]] : [true];
112+
texture._levelsUpdated = (texture.cubemap || texture.array) ? [Array(texture.slices).fill(true)] : [true];
113113
};
114114

115115
/**

src/platform/graphics/null/null-texture.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ class NullTexture {
1212

1313
loseContext() {
1414
}
15+
16+
uploadImmediate(device, texture, immediate) {
17+
}
1518
}
1619

1720
export { NullTexture };

src/platform/graphics/texture-utils.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Debug } from '../../core/debug.js';
22
import {
33
pixelFormatInfo,
4-
PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1,
5-
TEXTUREDIMENSION_3D
4+
PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1
65
} from './constants.js';
76

87
/**
@@ -70,11 +69,11 @@ class TextureUtils {
7069
/**
7170
* Calculate the GPU memory required for a texture.
7271
*
73-
* @param {string} dimension - Texture's dimension
7472
* @param {number} width - Texture's width.
7573
* @param {number} height - Texture's height.
7674
* @param {number} slices - Texture's slices.
7775
* @param {number} format - Texture's pixel format PIXELFORMAT_***.
76+
* @param {boolean} isVolume - True if the texture is a volume texture, false otherwise.
7877
* @param {boolean} mipmaps - True if the texture includes mipmaps, false otherwise.
7978
* @returns {number} The number of bytes of GPU memory required for the texture.
8079
* @ignore

src/platform/graphics/texture.js

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,6 @@ class Texture {
7878
/** @protected */
7979
_invalid = false;
8080

81-
/** @protected */
82-
_lockedLevel = -1;
83-
8481
/** @protected */
8582
_lockedMode = TEXTURELOCK_NONE;
8683

@@ -295,7 +292,7 @@ class Texture {
295292
if (this._levels) {
296293
this.upload(options.immediate ?? false);
297294
} else {
298-
this._levels = this.cubemap ? [[null, null, null, null, null, null]] : [null];
295+
this._levels = (this.cubemap || this.array) ? [Array(this._slices).fill(null)] : [null];
299296
}
300297

301298
// track the texture
@@ -833,7 +830,7 @@ class Texture {
833830

834831
// Force a full resubmission of the texture to the GPU (used on a context restore event)
835832
dirtyAll() {
836-
this._levelsUpdated = this.cubemap ? [[true, true, true, true, true, true]] : [true];
833+
this._levelsUpdated = (this.cubemap || this.array) ? [Array(this._slices).fill(true)] : [true];
837834

838835
this._needsUpload = true;
839836
this._needsMipmapsUpload = this._mipmaps;
@@ -850,6 +847,8 @@ class Texture {
850847
* to 0.
851848
* @param {number} [options.face] - If the texture is a cubemap, this is the index of the face
852849
* to lock.
850+
* @param {number} [options.slice] - If the texture is a texture array, this is the index of the
851+
* slice to lock.
853852
* @param {number} [options.mode] - The lock mode. Can be:
854853
* - {@link TEXTURELOCK_READ}
855854
* - {@link TEXTURELOCK_WRITE}
@@ -861,6 +860,7 @@ class Texture {
861860
// Initialize options to some sensible defaults
862861
options.level ??= 0;
863862
options.face ??= 0;
863+
options.slice ??= 0;
864864
options.mode ??= TEXTURELOCK_WRITE;
865865

866866
Debug.assert(
@@ -875,19 +875,38 @@ class Texture {
875875
this
876876
);
877877

878+
Debug.assert(
879+
options.level >= 0 && options.level < this._levels.length,
880+
'Invalid mip level',
881+
this
882+
);
883+
884+
Debug.assert(
885+
((this.cubemap || this.array) && options.mode === TEXTURELOCK_WRITE) ? options.level === 0 : true,
886+
'Only mip level 0 can be locked for writing for cubemaps and texture arrays',
887+
this
888+
);
889+
878890
this._lockedMode = options.mode;
879-
this._lockedLevel = options.level;
880891

881-
const levels = this.cubemap ? this._levels[options.face] : this._levels;
892+
const levels = this.cubemap ? this._levels[options.face] : this.array ? this._levels[options.slice] : this._levels;
882893
if (levels[options.level] === null) {
883894
// allocate storage for this mip level
884895
const width = Math.max(1, this._width >> options.level);
885896
const height = Math.max(1, this._height >> options.level);
886-
const depth = Math.max(1, (this._dimension === TEXTUREDIMENSION_3D ? this._slices : 1) >> options.level);
897+
const depth = Math.max(1, this.depth >> options.level);
887898
const data = new ArrayBuffer(TextureUtils.calcLevelGpuSize(width, height, depth, this._format));
888899
levels[options.level] = new (getPixelFormatArrayType(this._format))(data);
889900
}
890901

902+
if (this._lockedMode === TEXTURELOCK_WRITE) {
903+
if (this.cubemap || this.array) {
904+
this._levelsUpdated[0][options.face ?? options.slice] = true;
905+
} else {
906+
this._levelsUpdated[0] = true;
907+
}
908+
}
909+
891910
return levels[options.level];
892911
}
893912

@@ -897,22 +916,21 @@ class Texture {
897916
*
898917
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement|HTMLCanvasElement[]|HTMLImageElement[]|HTMLVideoElement[]} source - A
899918
* canvas, image or video element, or an array of 6 canvas, image or video elements.
900-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
901919
* Defaults to 0, which represents the base image source. A level value of N, that is greater
902920
* than 0, represents the image source for the Nth mipmap reduction level.
903921
* @param {boolean} [immediate] - When set to true it forces an immediate upload upon assignment. Defaults to false.
904922
*/
905-
setSource(source, mipLevel = 0, immediate = false) {
923+
setSource(source, immediate = false) {
906924
let invalid = false;
907925
let width, height;
908926

909-
if (this.cubemap) {
927+
if (this.cubemap || this.array) {
910928
if (source[0]) {
911929
// rely on first face sizes
912930
width = source[0].width || 0;
913931
height = source[0].height || 0;
914932

915-
for (let i = 0; i < 6; i++) {
933+
for (let i = 0; i < this._slices; i++) {
916934
const face = source[i];
917935
// cubemap becomes invalid if any condition is not satisfied
918936
if (!face || // face is missing
@@ -930,9 +948,9 @@ class Texture {
930948

931949
if (!invalid) {
932950
// mark levels as updated
933-
for (let i = 0; i < 6; i++) {
934-
if (this._levels[mipLevel][i] !== source[i])
935-
this._levelsUpdated[mipLevel][i] = true;
951+
for (let i = 0; i < this._slices; i++) {
952+
if (this._levels[0][i] !== source[i])
953+
this._levelsUpdated[0][i] = true;
936954
}
937955
}
938956
} else {
@@ -942,8 +960,8 @@ class Texture {
942960

943961
if (!invalid) {
944962
// mark level as updated
945-
if (source !== this._levels[mipLevel])
946-
this._levelsUpdated[mipLevel] = true;
963+
if (source !== this._levels[0])
964+
this._levelsUpdated[0] = true;
947965

948966
width = source.width;
949967
height = source.height;
@@ -958,23 +976,22 @@ class Texture {
958976
this._height = 4;
959977

960978
// remove levels
961-
if (this.cubemap) {
962-
for (let i = 0; i < 6; i++) {
963-
this._levels[mipLevel][i] = null;
964-
this._levelsUpdated[mipLevel][i] = true;
979+
if (this.cubemap || this.array) {
980+
for (let i = 0; i < this._slices; i++) {
981+
this._levels[0][i] = null;
982+
this._levelsUpdated[0][i] = true;
965983
}
966984
} else {
967-
this._levels[mipLevel] = null;
968-
this._levelsUpdated[mipLevel] = true;
985+
this._levels[0] = null;
986+
this._levelsUpdated[0] = true;
969987
}
970988
} else {
971989
// valid texture
972-
if (mipLevel === 0) {
973-
this._width = width;
974-
this._height = height;
975-
}
976990

977-
this._levels[mipLevel] = source;
991+
this._width = width;
992+
this._height = height;
993+
994+
this._levels[0] = source;
978995
}
979996

980997
// valid or changed state of validity
@@ -990,14 +1007,11 @@ class Texture {
9901007
* Get the pixel data of the texture. If this is a cubemap then an array of 6 images will be
9911008
* returned otherwise a single image.
9921009
*
993-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
994-
* Defaults to 0, which represents the base image source. A level value of N, that is greater
995-
* than 0, represents the image source for the Nth mipmap reduction level.
9961010
* @returns {HTMLImageElement} The source image of this texture. Can be null if source not
9971011
* assigned for specific image level.
9981012
*/
999-
getSource(mipLevel = 0) {
1000-
return this._levels[mipLevel];
1013+
getSource() {
1014+
return this._levels[0];
10011015
}
10021016

10031017
/**
@@ -1013,7 +1027,6 @@ class Texture {
10131027
if (this._lockedMode === TEXTURELOCK_WRITE) {
10141028
this.upload(immediate);
10151029
}
1016-
this._lockedLevel = -1;
10171030
this._lockedMode = TEXTURELOCK_NONE;
10181031
}
10191032

src/platform/graphics/webgl/webgl-texture.js

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ class WebglTexture {
407407

408408
const requiredMipLevels = texture.requiredMipLevels;
409409

410-
if (texture.array) {
410+
if (texture.array && !this._glCreated) {
411411
// for texture arrays we reserve the space in advance
412412
gl.texStorage3D(gl.TEXTURE_2D_ARRAY,
413413
requiredMipLevels,
@@ -445,10 +445,10 @@ class WebglTexture {
445445
if (device._isBrowserInterface(mipObject[0])) {
446446
// Upload the image, canvas or video
447447
for (face = 0; face < texture.slices; face++) {
448-
if (!texture._levelsUpdated[0][face])
448+
let src = mipObject[face];
449+
if (!texture._levelsUpdated[0][face] || !src)
449450
continue;
450451

451-
let src = mipObject[face];
452452
// Downsize images that are too large to be used as cube maps
453453
if (device._isImageBrowserInterface(src)) {
454454
if (src.width > device.maxCubeMapSize || src.height > device.maxCubeMapSize) {
@@ -487,10 +487,10 @@ class WebglTexture {
487487
// Upload the byte array
488488
resMult = 1 / Math.pow(2, mipLevel);
489489
for (face = 0; face < texture.slices; face++) {
490-
if (!texture._levelsUpdated[0][face])
490+
const texData = mipObject[face];
491+
if (!texture._levelsUpdated[0][face] || !texData)
491492
continue;
492493

493-
const texData = mipObject[face];
494494
if (texture._compressed) {
495495
if (this._glCreated && texData) {
496496
gl.compressedTexSubImage2D(
@@ -570,38 +570,40 @@ class WebglTexture {
570570
mipObject);
571571
}
572572
} else if (texture.array && typeof mipObject === "object") {
573-
if (texture._slices === mipObject.length) {
574-
if (texture._compressed) {
575-
for (let index = 0; index < texture._slices; index++) {
576-
gl.compressedTexSubImage3D(
577-
gl.TEXTURE_2D_ARRAY,
578-
mipLevel,
579-
0,
580-
0,
581-
index,
582-
Math.max(Math.floor(texture._width * resMult), 1),
583-
Math.max(Math.floor(texture._height * resMult), 1),
584-
1,
585-
this._glFormat,
586-
mipObject[index]
587-
);
588-
}
589-
} else {
590-
for (let index = 0; index < texture.slices; index++) {
591-
gl.texSubImage3D(
592-
gl.TEXTURE_2D_ARRAY,
593-
mipLevel,
594-
0,
595-
0,
596-
index,
597-
Math.max(Math.floor(texture._width * resMult), 1),
598-
Math.max(Math.floor(texture._height * resMult), 1),
599-
1,
600-
this._glFormat,
601-
this._glPixelType,
602-
mipObject[index]
603-
);
604-
}
573+
if (texture._compressed) {
574+
for (let index = 0; index < texture._slices; index++) {
575+
if (!texture._levelsUpdated[0][index] || !mipObject[index])
576+
continue;
577+
gl.compressedTexSubImage3D(
578+
gl.TEXTURE_2D_ARRAY,
579+
mipLevel,
580+
0,
581+
0,
582+
index,
583+
Math.max(Math.floor(texture._width * resMult), 1),
584+
Math.max(Math.floor(texture._height * resMult), 1),
585+
1,
586+
this._glFormat,
587+
mipObject[index]
588+
);
589+
}
590+
} else {
591+
for (let index = 0; index < texture.slices; index++) {
592+
if (!texture._levelsUpdated[0][index] || !mipObject[index])
593+
continue;
594+
gl.texSubImage3D(
595+
gl.TEXTURE_2D_ARRAY,
596+
mipLevel,
597+
0,
598+
0,
599+
index,
600+
Math.max(Math.floor(texture._width * resMult), 1),
601+
Math.max(Math.floor(texture._height * resMult), 1),
602+
1,
603+
this._glFormat,
604+
this._glPixelType,
605+
mipObject[index]
606+
);
605607
}
606608
}
607609
} else {
@@ -716,7 +718,7 @@ class WebglTexture {
716718
}
717719

718720
if (texture._needsUpload) {
719-
if (texture.cubemap) {
721+
if (texture.cubemap || texture.array) {
720722
for (let i = 0; i < texture.slices; i++)
721723
texture._levelsUpdated[0][i] = false;
722724
} else {

0 commit comments

Comments
 (0)