Skip to content

Commit 7f72c3f

Browse files
committed
Texture array updates WIP
1 parent 6c8c68c commit 7f72c3f

5 files changed

Lines changed: 96 additions & 79 deletions

File tree

src/framework/handlers/texture.js

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

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

116116
/**

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

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

1111
loseContext() {
1212
}
13+
14+
uploadImmediate(device, texture, immediate) {
15+
}
1316
}
1417

1518
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
/**
@@ -69,11 +68,11 @@ class TextureUtils {
6968
/**
7069
* Calculate the GPU memory required for a texture.
7170
*
72-
* @param {string} dimension - Texture's dimension
7371
* @param {number} width - Texture's width.
7472
* @param {number} height - Texture's height.
7573
* @param {number} slices - Texture's slices.
7674
* @param {number} format - Texture's pixel format PIXELFORMAT_***.
75+
* @param {boolean} isVolume - True if the texture is a volume texture, false otherwise.
7776
* @param {boolean} mipmaps - True if the texture includes mipmaps, false otherwise.
7877
* @returns {number} The number of bytes of GPU memory required for the texture.
7978
*/

src/platform/graphics/texture.js

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ class Texture {
8181
/** @protected */
8282
_invalid = false;
8383

84-
/** @protected */
85-
_lockedLevel = -1;
86-
8784
/** @protected */
8885
_lockedMode = TEXTURELOCK_NONE;
8986

@@ -297,7 +294,7 @@ class Texture {
297294
if (this._levels) {
298295
this.upload(options.immediate ?? false);
299296
} else {
300-
this._levels = this.cubemap ? [[null, null, null, null, null, null]] : [null];
297+
this._levels = (this.cubemap || this.array) ? [Array(this._slices).fill(null)] : [null];
301298
}
302299

303300
// track the texture
@@ -836,7 +833,7 @@ class Texture {
836833

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

841838
this._needsUpload = true;
842839
this._needsMipmapsUpload = this._mipmaps;
@@ -853,6 +850,8 @@ class Texture {
853850
* to 0.
854851
* @param {number} [options.face] - If the texture is a cubemap, this is the index of the face
855852
* to lock.
853+
* @param {number} [options.slice] - If the texture is a texture array, this is the index of the
854+
* slice to lock.
856855
* @param {number} [options.mode] - The lock mode. Can be:
857856
* - {@link TEXTURELOCK_READ}
858857
* - {@link TEXTURELOCK_WRITE}
@@ -864,6 +863,7 @@ class Texture {
864863
// Initialize options to some sensible defaults
865864
options.level ??= 0;
866865
options.face ??= 0;
866+
options.slice ??= 0;
867867
options.mode ??= TEXTURELOCK_WRITE;
868868

869869
Debug.assert(
@@ -878,19 +878,38 @@ class Texture {
878878
this
879879
);
880880

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

884-
const levels = this.cubemap ? this._levels[options.face] : this._levels;
895+
const levels = this.cubemap ? this._levels[options.face] : this.array ? this._levels[options.slice] : this._levels;
885896
if (levels[options.level] === null) {
886897
// allocate storage for this mip level
887898
const width = Math.max(1, this._width >> options.level);
888899
const height = Math.max(1, this._height >> options.level);
889-
const depth = Math.max(1, (this._dimension === TEXTUREDIMENSION_3D ? this._slices : 1) >> options.level);
900+
const depth = Math.max(1, this.depth >> options.level);
890901
const data = new ArrayBuffer(TextureUtils.calcLevelGpuSize(width, height, depth, this._format));
891902
levels[options.level] = new (getPixelFormatArrayType(this._format))(data);
892903
}
893904

905+
if (this._lockedMode === TEXTURELOCK_WRITE) {
906+
if (this.cubemap || this.array) {
907+
this._levelsUpdated[0][options.face ?? options.slice] = true;
908+
} else {
909+
this._levelsUpdated[0] = true;
910+
}
911+
}
912+
894913
return levels[options.level];
895914
}
896915

@@ -900,22 +919,21 @@ class Texture {
900919
*
901920
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement|HTMLCanvasElement[]|HTMLImageElement[]|HTMLVideoElement[]} source - A
902921
* canvas, image or video element, or an array of 6 canvas, image or video elements.
903-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
904922
* Defaults to 0, which represents the base image source. A level value of N, that is greater
905923
* than 0, represents the image source for the Nth mipmap reduction level.
906924
* @param {boolean} [immediate] - When set to true it forces an immediate upload upon assignment. Defaults to false.
907925
*/
908-
setSource(source, mipLevel = 0, immediate = false) {
926+
setSource(source, immediate = false) {
909927
let invalid = false;
910928
let width, height;
911929

912-
if (this.cubemap) {
930+
if (this.cubemap || this.array) {
913931
if (source[0]) {
914932
// rely on first face sizes
915933
width = source[0].width || 0;
916934
height = source[0].height || 0;
917935

918-
for (let i = 0; i < 6; i++) {
936+
for (let i = 0; i < this._slices; i++) {
919937
const face = source[i];
920938
// cubemap becomes invalid if any condition is not satisfied
921939
if (!face || // face is missing
@@ -933,9 +951,9 @@ class Texture {
933951

934952
if (!invalid) {
935953
// mark levels as updated
936-
for (let i = 0; i < 6; i++) {
937-
if (this._levels[mipLevel][i] !== source[i])
938-
this._levelsUpdated[mipLevel][i] = true;
954+
for (let i = 0; i < this._slices; i++) {
955+
if (this._levels[0][i] !== source[i])
956+
this._levelsUpdated[0][i] = true;
939957
}
940958
}
941959
} else {
@@ -945,8 +963,8 @@ class Texture {
945963

946964
if (!invalid) {
947965
// mark level as updated
948-
if (source !== this._levels[mipLevel])
949-
this._levelsUpdated[mipLevel] = true;
966+
if (source !== this._levels[0])
967+
this._levelsUpdated[0] = true;
950968

951969
width = source.width;
952970
height = source.height;
@@ -961,23 +979,22 @@ class Texture {
961979
this._height = 4;
962980

963981
// remove levels
964-
if (this.cubemap) {
965-
for (let i = 0; i < 6; i++) {
966-
this._levels[mipLevel][i] = null;
967-
this._levelsUpdated[mipLevel][i] = true;
982+
if (this.cubemap || this.array) {
983+
for (let i = 0; i < this._slices; i++) {
984+
this._levels[0][i] = null;
985+
this._levelsUpdated[0][i] = true;
968986
}
969987
} else {
970-
this._levels[mipLevel] = null;
971-
this._levelsUpdated[mipLevel] = true;
988+
this._levels[0] = null;
989+
this._levelsUpdated[0] = true;
972990
}
973991
} else {
974992
// valid texture
975-
if (mipLevel === 0) {
976-
this._width = width;
977-
this._height = height;
978-
}
979993

980-
this._levels[mipLevel] = source;
994+
this._width = width;
995+
this._height = height;
996+
997+
this._levels[0] = source;
981998
}
982999

9831000
// valid or changed state of validity
@@ -993,14 +1010,11 @@ class Texture {
9931010
* Get the pixel data of the texture. If this is a cubemap then an array of 6 images will be
9941011
* returned otherwise a single image.
9951012
*
996-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
997-
* Defaults to 0, which represents the base image source. A level value of N, that is greater
998-
* than 0, represents the image source for the Nth mipmap reduction level.
9991013
* @returns {HTMLImageElement} The source image of this texture. Can be null if source not
10001014
* assigned for specific image level.
10011015
*/
1002-
getSource(mipLevel = 0) {
1003-
return this._levels[mipLevel];
1016+
getSource() {
1017+
return this._levels[0];
10041018
}
10051019

10061020
/**
@@ -1016,7 +1030,6 @@ class Texture {
10161030
if (this._lockedMode === TEXTURELOCK_WRITE) {
10171031
this.upload(immediate);
10181032
}
1019-
this._lockedLevel = -1;
10201033
this._lockedMode = TEXTURELOCK_NONE;
10211034
}
10221035

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

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

462462
const requiredMipLevels = texture.requiredMipLevels;
463463

464-
if (texture.array) {
464+
if (texture.array && !this._glCreated) {
465465
// for texture arrays we reserve the space in advance
466466
gl.texStorage3D(gl.TEXTURE_2D_ARRAY,
467467
requiredMipLevels,
@@ -499,10 +499,10 @@ class WebglTexture {
499499
if (device._isBrowserInterface(mipObject[0])) {
500500
// Upload the image, canvas or video
501501
for (face = 0; face < texture.slices; face++) {
502-
if (!texture._levelsUpdated[0][face])
502+
let src = mipObject[face];
503+
if (!texture._levelsUpdated[0][face] || !src)
503504
continue;
504505

505-
let src = mipObject[face];
506506
// Downsize images that are too large to be used as cube maps
507507
if (device._isImageBrowserInterface(src)) {
508508
if (src.width > device.maxCubeMapSize || src.height > device.maxCubeMapSize) {
@@ -541,10 +541,10 @@ class WebglTexture {
541541
// Upload the byte array
542542
resMult = 1 / Math.pow(2, mipLevel);
543543
for (face = 0; face < texture.slices; face++) {
544-
if (!texture._levelsUpdated[0][face])
544+
const texData = mipObject[face];
545+
if (!texture._levelsUpdated[0][face] || !texData)
545546
continue;
546547

547-
const texData = mipObject[face];
548548
if (texture._compressed) {
549549
if (this._glCreated && texData) {
550550
gl.compressedTexSubImage2D(
@@ -624,38 +624,40 @@ class WebglTexture {
624624
mipObject);
625625
}
626626
} else if (texture.array && typeof mipObject === "object") {
627-
if (texture._slices === mipObject.length) {
628-
if (texture._compressed) {
629-
for (let index = 0; index < texture._slices; index++) {
630-
gl.compressedTexSubImage3D(
631-
gl.TEXTURE_2D_ARRAY,
632-
mipLevel,
633-
0,
634-
0,
635-
index,
636-
Math.max(Math.floor(texture._width * resMult), 1),
637-
Math.max(Math.floor(texture._height * resMult), 1),
638-
1,
639-
this._glFormat,
640-
mipObject[index]
641-
);
642-
}
643-
} else {
644-
for (let index = 0; index < texture.slices; index++) {
645-
gl.texSubImage3D(
646-
gl.TEXTURE_2D_ARRAY,
647-
mipLevel,
648-
0,
649-
0,
650-
index,
651-
Math.max(Math.floor(texture._width * resMult), 1),
652-
Math.max(Math.floor(texture._height * resMult), 1),
653-
1,
654-
this._glFormat,
655-
this._glPixelType,
656-
mipObject[index]
657-
);
658-
}
627+
if (texture._compressed) {
628+
for (let index = 0; index < texture._slices; index++) {
629+
if (!texture._levelsUpdated[0][index] || !mipObject[index])
630+
continue;
631+
gl.compressedTexSubImage3D(
632+
gl.TEXTURE_2D_ARRAY,
633+
mipLevel,
634+
0,
635+
0,
636+
index,
637+
Math.max(Math.floor(texture._width * resMult), 1),
638+
Math.max(Math.floor(texture._height * resMult), 1),
639+
1,
640+
this._glFormat,
641+
mipObject[index]
642+
);
643+
}
644+
} else {
645+
for (let index = 0; index < texture.slices; index++) {
646+
if (!texture._levelsUpdated[0][index] || !mipObject[index])
647+
continue;
648+
gl.texSubImage3D(
649+
gl.TEXTURE_2D_ARRAY,
650+
mipLevel,
651+
0,
652+
0,
653+
index,
654+
Math.max(Math.floor(texture._width * resMult), 1),
655+
Math.max(Math.floor(texture._height * resMult), 1),
656+
1,
657+
this._glFormat,
658+
this._glPixelType,
659+
mipObject[index]
660+
);
659661
}
660662
}
661663
} else {
@@ -770,7 +772,7 @@ class WebglTexture {
770772
}
771773

772774
if (texture._needsUpload) {
773-
if (texture.cubemap) {
775+
if (texture.cubemap || texture.array) {
774776
for (let i = 0; i < texture.slices; i++)
775777
texture._levelsUpdated[0][i] = false;
776778
} else {

0 commit comments

Comments
 (0)