Skip to content

Commit 29b1ce6

Browse files
committed
Texture array updates WIP
1 parent 24d4328 commit 29b1ce6

File tree

5 files changed

+96
-79
lines changed

5 files changed

+96
-79
lines changed

src/framework/handlers/texture.js

+4-4
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

+3
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

+2-3
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

+47-34
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
@@ -787,7 +784,7 @@ class Texture {
787784

788785
// Force a full resubmission of the texture to the GPU (used on a context restore event)
789786
dirtyAll() {
790-
this._levelsUpdated = this.cubemap ? [[true, true, true, true, true, true]] : [true];
787+
this._levelsUpdated = (this.cubemap || this.array) ? [Array(this._slices).fill(true)] : [true];
791788

792789
this._needsUpload = true;
793790
this._needsMipmapsUpload = this._mipmaps;
@@ -804,6 +801,8 @@ class Texture {
804801
* to 0.
805802
* @param {number} [options.face] - If the texture is a cubemap, this is the index of the face
806803
* to lock.
804+
* @param {number} [options.slice] - If the texture is a texture array, this is the index of the
805+
* slice to lock.
807806
* @param {number} [options.mode] - The lock mode. Can be:
808807
* - {@link TEXTURELOCK_READ}
809808
* - {@link TEXTURELOCK_WRITE}
@@ -815,6 +814,7 @@ class Texture {
815814
// Initialize options to some sensible defaults
816815
options.level ??= 0;
817816
options.face ??= 0;
817+
options.slice ??= 0;
818818
options.mode ??= TEXTURELOCK_WRITE;
819819

820820
Debug.assert(
@@ -829,19 +829,38 @@ class Texture {
829829
this
830830
);
831831

832+
Debug.assert(
833+
options.level >= 0 && options.level < this._levels.length,
834+
'Invalid mip level',
835+
this
836+
);
837+
838+
Debug.assert(
839+
((this.cubemap || this.array) && options.mode === TEXTURELOCK_WRITE) ? options.level === 0 : true,
840+
'Only mip level 0 can be locked for writing for cubemaps and texture arrays',
841+
this
842+
);
843+
832844
this._lockedMode = options.mode;
833-
this._lockedLevel = options.level;
834845

835-
const levels = this.cubemap ? this._levels[options.face] : this._levels;
846+
const levels = this.cubemap ? this._levels[options.face] : this.array ? this._levels[options.slice] : this._levels;
836847
if (levels[options.level] === null) {
837848
// allocate storage for this mip level
838849
const width = Math.max(1, this._width >> options.level);
839850
const height = Math.max(1, this._height >> options.level);
840-
const depth = Math.max(1, (this._dimension === TEXTUREDIMENSION_3D ? this._slices : 1) >> options.level);
851+
const depth = Math.max(1, this.depth >> options.level);
841852
const data = new ArrayBuffer(TextureUtils.calcLevelGpuSize(width, height, depth, this._format));
842853
levels[options.level] = new (getPixelFormatArrayType(this._format))(data);
843854
}
844855

856+
if (this._lockedMode === TEXTURELOCK_WRITE) {
857+
if (this.cubemap || this.array) {
858+
this._levelsUpdated[0][options.face ?? options.slice] = true;
859+
} else {
860+
this._levelsUpdated[0] = true;
861+
}
862+
}
863+
845864
return levels[options.level];
846865
}
847866

@@ -851,22 +870,21 @@ class Texture {
851870
*
852871
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement|HTMLCanvasElement[]|HTMLImageElement[]|HTMLVideoElement[]} source - A
853872
* canvas, image or video element, or an array of 6 canvas, image or video elements.
854-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
855873
* Defaults to 0, which represents the base image source. A level value of N, that is greater
856874
* than 0, represents the image source for the Nth mipmap reduction level.
857875
* @param {boolean} [immediate] - When set to true it forces an immediate upload upon assignment. Defaults to false.
858876
*/
859-
setSource(source, mipLevel = 0, immediate = false) {
877+
setSource(source, immediate = false) {
860878
let invalid = false;
861879
let width, height;
862880

863-
if (this.cubemap) {
881+
if (this.cubemap || this.array) {
864882
if (source[0]) {
865883
// rely on first face sizes
866884
width = source[0].width || 0;
867885
height = source[0].height || 0;
868886

869-
for (let i = 0; i < 6; i++) {
887+
for (let i = 0; i < this._slices; i++) {
870888
const face = source[i];
871889
// cubemap becomes invalid if any condition is not satisfied
872890
if (!face || // face is missing
@@ -884,9 +902,9 @@ class Texture {
884902

885903
if (!invalid) {
886904
// mark levels as updated
887-
for (let i = 0; i < 6; i++) {
888-
if (this._levels[mipLevel][i] !== source[i])
889-
this._levelsUpdated[mipLevel][i] = true;
905+
for (let i = 0; i < this._slices; i++) {
906+
if (this._levels[0][i] !== source[i])
907+
this._levelsUpdated[0][i] = true;
890908
}
891909
}
892910
} else {
@@ -896,8 +914,8 @@ class Texture {
896914

897915
if (!invalid) {
898916
// mark level as updated
899-
if (source !== this._levels[mipLevel])
900-
this._levelsUpdated[mipLevel] = true;
917+
if (source !== this._levels[0])
918+
this._levelsUpdated[0] = true;
901919

902920
width = source.width;
903921
height = source.height;
@@ -912,23 +930,22 @@ class Texture {
912930
this._height = 4;
913931

914932
// remove levels
915-
if (this.cubemap) {
916-
for (let i = 0; i < 6; i++) {
917-
this._levels[mipLevel][i] = null;
918-
this._levelsUpdated[mipLevel][i] = true;
933+
if (this.cubemap || this.array) {
934+
for (let i = 0; i < this._slices; i++) {
935+
this._levels[0][i] = null;
936+
this._levelsUpdated[0][i] = true;
919937
}
920938
} else {
921-
this._levels[mipLevel] = null;
922-
this._levelsUpdated[mipLevel] = true;
939+
this._levels[0] = null;
940+
this._levelsUpdated[0] = true;
923941
}
924942
} else {
925943
// valid texture
926-
if (mipLevel === 0) {
927-
this._width = width;
928-
this._height = height;
929-
}
930944

931-
this._levels[mipLevel] = source;
945+
this._width = width;
946+
this._height = height;
947+
948+
this._levels[0] = source;
932949
}
933950

934951
// valid or changed state of validity
@@ -944,14 +961,11 @@ class Texture {
944961
* Get the pixel data of the texture. If this is a cubemap then an array of 6 images will be
945962
* returned otherwise a single image.
946963
*
947-
* @param {number} [mipLevel] - A non-negative integer specifying the image level of detail.
948-
* Defaults to 0, which represents the base image source. A level value of N, that is greater
949-
* than 0, represents the image source for the Nth mipmap reduction level.
950964
* @returns {HTMLImageElement} The source image of this texture. Can be null if source not
951965
* assigned for specific image level.
952966
*/
953-
getSource(mipLevel = 0) {
954-
return this._levels[mipLevel];
967+
getSource() {
968+
return this._levels[0];
955969
}
956970

957971
/**
@@ -967,7 +981,6 @@ class Texture {
967981
if (this._lockedMode === TEXTURELOCK_WRITE) {
968982
this.upload(immediate);
969983
}
970-
this._lockedLevel = -1;
971984
this._lockedMode = TEXTURELOCK_NONE;
972985
}
973986

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

+40-38
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)